17 Oct 2017 by 晓晨
全文都是猜的。全文省略“我觉得”、“我猜”等词。
Constant stored properties 不在讨论范围,虽然本身是 stored properties 的一种,但它相对简单,不能被覆盖,基本可以看作普通常量。见 Swift 中的咬文嚼字。
下文中的所有 stored properties 都是指 variable stored properties。
不管是 computed properties 还是 stored properties,本质上都是 computed properties,都是 getters 和 setters 方法,与 Objective-C 和其它语言中的 getters、setters 方法以及可能的 backing storage 对应。
对于 stored properties,Swift 为我们生成 getters、setters 和 backing storage。
对于 stored properties,我们还可以为其提供 property observers,willSet 和/或 didSet,Swift 会在生成的 setter 中调用这两个方法。这两个 observer 方法像是一种特别的可选代理方法。
对于 computed properties,我们不能为其提供 property observers,因为 computed properties 的 setter 是我们通过 set 方法为其提供的,由于 setter 不再是 Swift 为我们生成的,当然也就不会调用 observers 方法。我们完全可以自己在 set 方法中调用 willSet 或者 didSet,或者干脆直接把两个函数的内容写进 set 方法。
因为 Swift 为 stored properties 自动生成 setters,它会有一些特殊处理。
当我们在定义 stored properties 时为其提供默认值或者在构造器中为其设置初始值,都不会调用 observers。
有时候我们可能会在 observers 中修改该属性值,按理说,修改属性值应该会导致 observers 方法再次被调用,从而反复不断被调用,但是 Swift 不知道通过什么办法避免了这个问题,但这点只在没有覆盖属性的情况下发生。
如果为覆盖属性提供 observers,我们 observers 中修改了当前属性,会导致无休止的递归调用。我们可以通过 super 关键字修改父类的该属性,从而避免无限递归调用。(因为我们这里只是在讨论 overriding property observers,而非 overriding property getters and setters,所以这里引用父类的该属性应该不会造成含义上的改变,但需要考虑当前类的 observers 将不再被调用。)
接下来讨论属性的覆盖。
因为属性本质上是方法,所以属性覆盖本质上是方法覆盖,是 getter 和 setter 方法的覆盖。
你可以通过覆盖属性来做两类事,提供自定义的 getter 和 setter,或者添加 observers。
注意对于 overriding property getters and setters 和 overriding property observers 的理解,是通过覆盖属性来提供 getters and setters 或者通过覆盖属性来添加 observers。而不是覆盖了父类属性的 getters and setters 和 observers。见 Swift 中的咬文嚼字
Overriding property getters and setters,在当前类中以 computed observers 的写法(get 和/或 set 方法)为覆盖的属性提供 getters 和/或 setters 方法,这会覆盖父类中该属性的 getters 和/或 setters,如果父类的这个属性是 storage property,那它的 getter 和 setter 是 Swift 生成的,如果是 computed property,它的 getter 和 setter 方法就是 get 和 set 方法,不管怎样,都是对 getters 和/或 setter 方法的覆盖。
Overriding property observers,这种情况下,需要在当前类中为该属性提供 observers,从而在当该属性被修改时,observers 被调用。
在当前类中提供的 observers 方法中无法调用父类中的 observers,因为这不是对 observers 方法的覆盖。
为了使当前类中该属性被调用时父类和当前类的 observers 都被调用,该属性的 setter 会由 Swift 生成的 setter 覆盖,其中的实现使得该属性被修改时,当前类 willSet、父类 willSet、父类 didSet、当前类 didSet 被依次调用。
全局变量、局部变量、类型属性,本质上也都和实例属性一样,都是 getter、setter 方法以及可能的 backing storage。
类型属性有两个关键字 static 和 class,除了表示能否被继承,没有本质差别,都是表示该属性所属这个类型,而非某个实例。
static 表示不能被继承,class 表示可以被继承,注意,它们只是在说自己能不能被继承,而非自己能不能继承别人。static 属性也可以覆盖 class 属性。
但是对于 class 属性,只能是 computed properties,不能是 stored properties,至于为什么是这样,我想了很久也还是没能想出一个合理的解释或者例子,可以关注下这个问题有没有新答案。
关于属性本质上是函数这点还从下面的例子中得到了印证,
var i = 0
(i = 1) == ()
一个没有返回值的函数其实是返回了一个 Void 类型的 ()
(empty tuple),上面例子中的 i = 1
也有同样的返回值,一个解释就是这个表达式实际上是调用了变量 i
的 setter,可能是这样,i.set(1)
,也就是说 i
本质上是它的 getter 和 setter 两个函数。
另一个例子,在给可选值的属性赋值时,如果该可选值为 nil,那么这个赋值语句的右值不会被求值。
func returnOne() -> Int {
print("returnOne called")
return 1
}
class C {
var i = 0
}
let c: C? = nil
c?.i = returnOne() // returnOne 不会被调用
var i: Int? = nil
i? = returnOne() // returnOne 不会被调用