結論
init/deinit内でプロパティに代入を行っても、willSet/didSetは呼ばれないので注意しましょう。
ただし、スーパークラスのプロパティのwillSet/didSetは呼ばれるようです。
公式ガイドの注意書き
公式ガイド(The Swift Programming LanguageのProperties)に、以下のような注意書きがあります。
NOTE
The willSet and didSet observers of superclass properties are called when a property is set in a subclass initializer, after the superclass initializer has been called. They are not called while a class is setting its own properties, before the superclass initializer has been called.1
注意
サブクラスのイニシャライザ内で、スーパークラスのイニシャライザが呼ばれた後に、プロパティがセットされたとき、スーパークラスのプロパティのwillSetとdidSetのオブザーバーが呼ばれます。スーパークラスのイニシャライザが呼ばれる以前に、自身のプロパティをセットしている間は呼ばれません。2
なるほど。スーパークラスのプロパティのdidSet/willSetについては、サブクラス内のイニシャライザでスーパクラスのイニシャライザを呼んだ後であれば、機能するということらしいです。
クラス自身で定義したプロパティのdidSet/willSetについては、イニシャライザで呼ばれないのは当然だろということか、記述が見当たりませんでした。
コードで確認
確かにスーパークラスのプロパティのdidSetについては呼ばれました。
class Animal {
var age: Int {
didSet {
print(#function, age, oldValue)
}
}
init(age: Int) {
self.age = age // -> 呼ばれない
}
}
class Tiger: Animal {
var family: String {
didSet {
print(#function, family, oldValue)
}
}
init(age: Int, family: String) {
self.family = family // -> 呼ばれない
super.init(age: age) // -> 呼ばれない
self.age += 0 // -> didSetが呼ばれる!!
self.family += "" // -> 呼ばれない
}
deinit {
print(#function)
age = 0 // -> 呼ばれる
family = "" // -> 呼ばれない
}
}
どういうときに注意すべき?
例えば、Timerをdeinitでinvalidateしたいときに、以下のような書き方をすると呼ばれないので注意しましょう。
class ViewController2: UIViewController {
var timer: Timer? {
willSet {
timer?.invalidate()
}
}
deinit {
timer = nil // nilを代入してもwillSetは呼ばれない!
// -> timer?.invalidate()
}
...
}
参考
Swiftでdeinit時にメンバ変数(property)のdidSetが呼ばれない気がした
Can I use didSet in deinit?
-
[The Swift Programming Language - Properties](https://docs.swift.org/swift-book/LanguageGuide/Properties ↩
-
太字による強調は訳者による ↩