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参数传递,那么willSet和didSet总会被调用。因为in-out参数的copy-in、copy-out内存模型使得函数在结束时总会回写属性的值。
关于全局和局部变量
全局的变量或常量总是惰性计算的,类似于惰性存储的属性,不同的是全局的常量和变量不需要声明为lazy
局部常量和变量永远不会惰性计算
Type Property
Type属性必须给初值,因为没有initializer来初始化它。Stored type属性第一次访问是惰性的,它们能保证只初始化一次。
继承
Swift的类没有继承一个通用的基类。如果没有给定义的类指定superclass的话,那么你定义的这个类就是自己的基类。
Overriding
- 重写Property Getters 和 Setters
可以通过提供自定义的getter或setter重写任何继承的属性,不管这个属性是stored还是computed属性。子类并不是知道继承属性是stored还是computed,它只知道属性的类型和名字。
可以通过提供getter和setter来重写只读属性为可读写的属性。但是,不能将可读写的属性重写为只读的。
如果你提供了属性setter方法的重写,你也必须提供getter方法。如果你不想在getter方法内修改继承属性的值,只需要在方法内简单地返回super.someProperty。
- 重写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
类间相互引用的三种情况
- 两个属性都可以是nil,互相引用时可能会导致强引用循环。这个场景最好用weak reference进行解决(人和住宅的例子)
- 两个属性,一个可以是nil,另一个不能为nil,这种场景下最好用unowned reference(人和信用卡的例子)
- 两个属性都必须不为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
简单的方式,
- 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
- 让现有类型服从某一协议