美文网首页
学Swift速记

学Swift速记

作者: 解放者莫雷尔 | 来源:发表于2016-08-15 18:48 被阅读0次

    Property(属性)

    属性将值与特定的类、结构体或枚举联系起来。Stored属性将常量或变量存储作为实例的一部分,而computed属性计算而不是存储值。Computed属性是类、结构体或枚举提供的,Stored属性只有类或结构体提供 。

    Stored或Computed属性通常与特定类型的实例相关联。但是,属性也可以与类型本身相关联。这样的属性称为type property

    最简单的情况下,stored property就是作为一个类、结构体的实例的一部分而存储。

    必须将一个lazy属性声明为var,因为它的值可能在实例初始化完成后才能获得。constant要求在初始化完成时就已经有值,因此不能是lazy的。

    Lazy属性在初始化依靠于外部因素时比较有用,如果一个属性的值要复杂计算开销很大时,使用lazy也是很合适的

    注意,lazy属性的初始化过程不是线程安全的!

    Objective C中提供了两个方式来存储值和引用,除了属性,你还可以使用instance variable as a backing store。
    Swift将这些概念统一到了一个单独的属性声明中。一个Swift属性没有对应的instance variable,属性的backing store不能直接被访问。这种方式避免了值在不同上下文如何访问的困惑。属性的所有信息,都作为类型定义的一部分在同一个位置定义。

    除了Stored属性,类、结构体和枚举还提供computed属性,它实际上不存储值,只是提供一个getter方法(setter方法可选)来间接获取和设置其它属性的值。

    如果将一个有observer的属性作为函数的in-out参数传递,那么willSetdidSet总会被调用。因为in-out参数的copy-in、copy-out内存模型使得函数在结束时总会回写属性的值。

    关于全局和局部变量

    全局的变量或常量总是惰性计算的,类似于惰性存储的属性,不同的是全局的常量和变量不需要声明为lazy
    局部常量和变量永远不会惰性计算

    Type Property

    Type属性必须给初值,因为没有initializer来初始化它。Stored type属性第一次访问是惰性的,它们能保证只初始化一次。

    继承

    Swift的类没有继承一个通用的基类。如果没有给定义的类指定superclass的话,那么你定义的这个类就是自己的基类。

    Overriding

    1. 重写Property Getters 和 Setters
      可以通过提供自定义的getter或setter重写任何继承的属性,不管这个属性是stored还是computed属性。子类并不是知道继承属性是stored还是computed,它只知道属性的类型和名字。
      可以通过提供getter和setter来重写只读属性为可读写的属性。但是,不能将可读写的属性重写为只读的。

    如果你提供了属性setter方法的重写,你也必须提供getter方法。如果你不想在getter方法内修改继承属性的值,只需要在方法内简单地返回super.someProperty

    1. 重写Property Observers

    不能给继承的常量stored属性或者只读computed属性添加observers。这些属性的值是不能修改的
    同时,你不能同时重写一个属性的setter和observer。如果你想观测属性值的改变,同时也想提供自定义的setter方法,你只需要在setter方法中观测值的改变就好了。

    3.重写方法

    禁止Overrides

    可以通过设置method、property或subscript为final来禁止重写。可以将class标记为final,这样任何类都不能继承这个类。

    Structure也支持class的很多行为,比如methods和initializer,最重要的一点区别就是structure传递的时候总是被拷贝的,但是class是通过引用。Structure适合一些轻量级的不需要继承和类型转换的数据结构。

    Protocol

    A protocol defines a blueprint of methods,properties and other requirements that suit a particular task or piece of functionality.

    Protocol可以被class、structure和enumeration实现

    protocol ExampleProtocol{
        var simpleDescription:String {get}
        func adjust()
    }
    

    {get}表示这个属性是只读的,不能被修改

    Protocol are first-class types,which means they can be treated like other named types.即协议也跟普通的数据类型一样被对待。比如,也可以创建一个协议的数组等。

    ARC

    类间相互引用的三种情况

    1. 两个属性都可以是nil,互相引用时可能会导致强引用循环。这个场景最好用weak reference进行解决(人和住宅的例子)
    2. 两个属性,一个可以是nil,另一个不能为nil,这种场景下最好用unowned reference(人和信用卡的例子)
    3. 两个属性都必须不为nil时。这种情况下,一个类使用unowned reference,另一个使用implicit unwrapped optional。

    Initialization

    Classes和Structures必须在实例创建时给所有的stored属性赋初始值。可以在initializer中赋值,也可以在声明属性时设置默认值。

    当设置stored属性默认值或在initializer中初始化时,属性的observer不会被调用

    Initializer delegation:Initializer可以调用其他的initializer来完成初始化过程,避免重复的代码

    Value types(structures和enumerations)不支持继承,所以initialize delegate只是调用自己定义的其他initializer。

    如果你想同时用默认的default initializer和memberwise initializer、自定义的initializer来初始化,那么可以将自定义的initializer放到extension中。

    Designated Initializer And Convenience Initializer

    Designated Initializer是类的主要初始化方法,它完全初始化所有的属性,调用superclass的initializer来传递superclass chain。每个类都至少要有一个。
    Convenience Initializer是次要的,支持性的initializer。可以定义它来调用designated initializer(设置某些属性为默认值)。

    Initializer Delegation For Class Types

    规则1
    Designated initializer必须调用 immediate superclass的designated initializer
    规则2
    Convenience initializer必须调用同一个类的initializer
    规则3
    Convenience initializer必须调用designated initializer

    Initializer.png

    简单的方式,

    • Designated initializer必须delegate up
    • Convenience initializer必须delegate cross

    Two-Phase Initialization

    Swift的编译器为了确保two-phase initialization正确执行而进行一些安全性检查。
    Safety Check1
    Designated initializer必须确保在initializer传递到superclass前所有的属性都已经初始化了。
    只要所有的stored属性都初始化,就认为这个对象的内存已经完全初始化了。所以,designated initializer必须确保在传递初始化链前所有的属性都初始化。
    Safety Check2
    Designated initializer必须在给继承属性赋值前调用superclass initializer,如果不这么做,赋值会被superclass的initializer覆盖。
    Safety Check3
    Convenience Initializer必须在给任何属性赋值前调用其他的initializer。如果不这么做,赋值会被designated initializer覆盖。
    Safety Check4
    Initializer不能调用任何实例方法,读取任何实例属性,或引用self,直到initializer的first phase完成。

    Swift初始化主要分两步,第一步给每个stored属性赋一个初始值,一旦每个属性的初始值确定,第二个步骤就开始了,在实例被使用之前,每个类都有机会自定义stored属性。

    Swift的两步初始化过程与OC的类似。不同点在于第一步时,OC给每个属性赋zero或null。Swift的初始化更灵活,可以有自定义的初始值。

    Phase 1

    • Initializer被调用
    • 分配内存,但内存还未初始化
    • designated intializer确认所有的stored属性都有值,这些属性的内存开始初始化
    • designated initializer交给superclass的initializer给它自己的属性进行同样的任务
    • 继承链进行传递
    • 继承链到顶之后,链上的最后一个类确保所有的stored属性都有值,实例的内存被认为完全初始化了

    Phase 2

    • 从继承链往回遍历,每个designated initializer都有机会自定义实例。initializer可以访问self,修改属性,调用实例方法等等
    • 最后,任何的convenience initializer可以选择自定义实例

    Swift的subclass默认是不继承superclass的initializer的。但是特定情况下superclass的initializer会被继承。假设subclass引入的新的属性都有默认值,那么会应用下面规则:
    规则1
    如果subclass没有定义任何的initializer,它自动继承superclass所有的designated initializer。
    规则2
    如果subclass提供了所有的designated initializer的实现(通过规则1或自定义),那么它自动继承所有的convenience initializer。(以convenience的方式也可以)

    不能定义相同参数和名称的failable、nonfailable initializer

    failable initializer创建一个optional值。

    如果使用closure来初始化属性,牢记在closure执行完毕之前其余的实例都没有被初始化。这意味着在closure中不能访问其他任意属性,即使它有默认值。也不能使用self属性,或调用实例方法。

    Closure

    Global和nested function其实是特殊形式的closure。Closure有三种形式:

    • Global function,有名称,不捕获任何值
    • Nested function,有名称,捕获包含它的function内的值
    • Closure,没名称,捕获上下文的值

    Trailing Closure

    如果closure作为function的最后一个参数,并且closure定义非常长,那么可以使用trailing closure的技巧,将closure定义写在函数的外面。

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // function定义
    }
     
    // here's how you call this function without using a trailing closure:
     
    func someFunctionThatTakesAClosure({
        // closure定义
    })
     
    // here's how you call this function with a trailing closure instead:
     
    func someFunctionThatTakesAClosure() {
        // trailing closure's 定义
    }
    

    Nonescaping Closures

    当closure作为function的参数,但是在function返回之后才被调用,就说这个closure escape a function。

    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    // Prints "5"
    
    ##Autoclosures
    Autoclose是一个包含表达式、作为函数参数的的closure。它不包含任何参数,当调用时,它返回表达式的值。Autoclosure能延迟evaluation。
    //Autoclosure,此时closure不执行
    let customerProvider = { customersInLine.removeAtIndex(0) }
    print(customersInLine.count)
    // Prints "5"
    
    //此时closure被真正调用 
    print("Now serving \(customerProvider())!")
    // Prints "Now serving Chris!"
    print(customersInLine.count)
    // Prints "4"
    
    • AnyObject可以表示任意类的实例
    • Any可以表示任何类型的实例,包括函数类型

    Extensions

    Extensions可以给现有的class、structure、enumeration或protocol添加新的功能。与OC中的category类似,但是没有名字。
    Extensions可以:

    • 添加computed实例属性和类型属性
    • 定义实例方法和类方法
    • 提供新的initializer
    • 定义subscript
    • 定义和使用新的nexted types
    • 让现有类型服从某一协议

    相关文章

      网友评论

          本文标题:学Swift速记

          本文链接:https://www.haomeiwen.com/subject/tryfsttx.html