Swift の Property Observers の細かい動き

Swift を書くときは Property Observers (willSet, didSet のやつのこと)をよく使うと思います。 今回はその Property Observers の細かい挙動についてクイズ形式で説明したいと思います。

初期化時の動きなど意外と知らないことも多いのではないかと思います。

Swift の Property に関する公式ドキュメントはこちらです。

動作確認はこちらのサイトを使うとコピペで簡単にできます。

それでは早速細かい動作について説明していきたいと思います。

継承した Class の property を override した場合

以下のようなコードの場合、Super Class, Sub Class それぞれの didSet はどうなる?

class Super {
  var value: Int = 0 {
    didSet { print("super:didSet") }
  }
}
class Sub: Super {
  override var value: Int {
    didSet { print("sub:didSet") }
  }
}
let sub = Sub()
sub.value = 1
// 何が出力される?

答え(クリックで展開)

super:didSet
sub:didSet

Super Class, Sub Class の didSet が両方呼ばれる.

Compute Property には Property Observers つけれる?

class Super {
  var _aaaa: Double = 0
  var aaaa: Double {
    get { _aaaa }
    set(newValue) { _aaaa = newValue }
    didSet { print("aaaa") }
  }
}

答え(クリックで展開)

エラーになってつけれない.

'didSet' cannot be provided together with a getter

継承した場合.

class Super {
  var _aaaa: Double = 0
  var aaaa: Double {
    get { _aaaa }
    set(newValue) { _aaaa = newValue }
  }
}

class Sub: Super {
  override var aaaa: Double {
    didSet {
      print("aaaa")
    }
  }
}

答え(クリックで展開)

Sub Class ではつけれる.

イニシャライザ内で変更すると willSet/didSet 呼ばれる?

class Super {
  var value: Int {
    didSet { print("didSet:value", value) }
  }

  init() {
    // didSet 呼ばれる?
    value = 1
  }
}

class Sub: Super {
  var valueSub: Int {
    didSet { print("didSet:valueSub", valueSub) }
  }

  override init() {

    // didSet 呼ばれる?
    valueSub = 0
    
    super.init()

    // didSet 呼ばれる?
    value = 10

    // didSet 呼ばれる?
    valueSub = 1

    // didSet 呼ばれる?
    add(3)
  }

  func add(_ v: Int) {
    value += v 
    valueSub += v
  }
}

let sub = Sub()

答え(クリックで展開)

主力結果

didSet:value 10
didSet:value 13
didSet:valueSub 4
  • init 内では基本呼ばれない
  • super.init 後は呼ばれる
  • インスタンスメソッドを経由すると呼ばれる

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 aren’t called while a class is setting its own properties, before the superclass initializer has been called.

Array.append とかの操作で呼ばれる?

Array などに対するミュータブルな操作で willSet/didSet 呼ばれる?

class Super {
  var array: [Int] = [] {
    didSet { print("didSet:array", array) }
  }
}

let s = Super()

// didSet 呼ばれる?
s.array.append(1)

答え(クリックで展開)

呼ばれる.

出力結果.

didSet:array [1]

@IBOutlet は didSet 呼ばれる?

class HogeViewController: UIViewController {
    @IBOutlet var tableView: UITableView! {
        didSet {
            print("didSet:TableView")
        }
    }
} 

答え(クリックで展開)

呼ばれる. 以下のように書くと viewDidLoad がスッキリして見やすくなる.

  @IBOutlet var tableView: UITableView! {
        didSet {
            tableView.dataSource = self
            tableView.delegate = self
        }
    }

まとめ

  • override した Property に Property Observer を設定できる
    • Super クラスの Property Observer は消えない
  • Compute Property に Property Observer はつけれない
    • ただし継承した Sub Class ではつけれる
  • イニシャライザ内で変数を初期化した場合は Property Observer は呼ばれない
    • ただしイニシャライザ内でインスタンスメソッドを呼ぶと Property Observer は呼ばれる
  • Property にミュータブルな操作をすると Property Observer は呼ばれる