属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
你可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,你也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为你可以直接通过它的 setter 监控和响应值的变化。属性重写请参考 重写。
可以为属性添加其中一个或两个观察器:
willSet
在新的值被设置之前调用didSet
在新的值被设置之后调用
willSet
观察器会将新的属性值作为常量参数传入,在 willSet
的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称 newValue
表示。
同样,didSet
观察器会将旧的属性值作为参数传入,可以为该参数指定一个名称或者使用默认参数名 oldValue
。如果在 didSet
方法中再次对该属性赋值,那么新值会覆盖旧的值。
注意
在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的
willSet
和didSet
观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。
有关构造器代理的更多信息,请参考 值类型的构造器代理 和 类的构造器代理。
下面是一个 willSet
和 didSet
实际运用的例子,其中定义了一个名为 StepCounter
的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("将 totalSteps 的值设置为 \(newTotalSteps)") } didSet { if totalSteps > oldValue { print("增加了 \(totalSteps - oldValue) 步") } } } } let stepCounter = StepCounter() stepCounter.totalSteps = 200 // 将 totalSteps 的值设置为 200 // 增加了 200 步 stepCounter.totalSteps = 360 // 将 totalSteps 的值设置为 360 // 增加了 160 步 stepCounter.totalSteps = 896 // 将 totalSteps 的值设置为 896 // 增加了 536 步
StepCounter
类定义了一个叫 totalSteps
的 Int
类型的属性。它是一个存储属性,包含 willSet
和 didSet
观察器。
当 totalSteps
被设置新值的时候,它的 willSet
和 didSet
观察器都会被调用,即使新值和当前值完全相同时也会被调用。
例子中的 willSet
观察器将表示新值的参数自定义为 newTotalSteps
,这个观察器只是简单的将新的值输出。
didSet
观察器在 totalSteps
的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。didSet
没有为旧值提供自定义名称,所以默认值 oldValue
表示旧值的参数名。
注意
如果将带有观察器的属性通过 in-out 方式传入函数,
willSet
和didSet
也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 输入输出参数。