Swift の didSet の細かい挙動: 複数の値の変更を1回の didSet にまとめたい

SwiftのプロパティオブザーバーdidSetは、プロパティの値が変更された後に呼び出される便利な機能です。しかし、複数のプロパティを持つ構造体がクラス内のプロパティとして宣言されている場合、各プロパティの変更ごとにdidSetが呼び出されます。これは場合によっては非効率的であるため、これを回避する方法を紹介します。

背景

例えば、DataContainerという構造体があり、その中にvalueとflagというプロパティがあるとします。これをクラス内でプロパティとして使用し、それぞれのプロパティを変更すると、変更のたびにdidSetが呼び出されます。これが大きな配列や頻繁に呼ばれるメソッドである場合、パフォーマンスに影響を与える可能性があります。

具体例

まず、問題となるコードを見てみましょう。

import Foundation

struct DataContainer {
  var value: Int = 0
  var flag: Bool = false
}

class Model {
  var data = DataContainer() {
    didSet {
      print("didSet", data)
    }
  }

  func changeValue() {

    data.value += 1
    data.flag.toggle()
  }
}

let model = Model()

model.changeValue()

このコードを実行すると、data.valueとdata.flagの変更ごとにdidSetが呼び出されることが確認できます。

実行結果

didSet DataContainer(value: 1, flag: false) // data.value += 1
didSet DataContainer(value: 1, flag: true) // data.flag.toggle()

解決方法

この問題を解決するためには、mutating funcを使用して複数のプロパティ変更を一度に行うことができます。これにより、didSetが一度だけ呼び出されるようになります。

修正後のコードは以下の通りです。

まとめ

mutating funcを使用して複数のプロパティ変更をまとめることで、didSetが一度だけ呼び出されるようになり、パフォーマンスが向上します。このテクニックは、特にパフォーマンスが重要な場面で役立ちます。