LoginSignup
62
58

More than 5 years have passed since last update.

【Swift】KVOでプロパティーの変更を監視する

Posted at

KVOを使ってみる

KVOとはKey-Value Observingの略で、プロパティーの値の変化を通知してくれる仕組みです。

実装は下のように行います。
addObserverの引数にプロパティー名を渡すと、そのプロパティーが変更された時にobserveValueForKeyPathメソッドが呼ばれるようになります。

class MyClass: NSObject { // NSObjectのサブクラス
    dynamic var prop = 0 // 監視対象にはdynamicを付ける必要がある

    override init() {
        super.init()
        addObserver(self, forKeyPath: "prop", options: [.New, .Old], context: nil)
    }

    deinit {
        removeObserver(self, forKeyPath: "prop")
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {

        print(keyPath) // Optional("prop")
        print(object) // Optional(<MyApp.MyClass: 0x7fdad1c013d0>)
        print(change) // Optional(["old": 0, "new": 10, "kind": 1])
    }
}

let obj = MyClass()
obj.prop = 10 // プロパティーの値を変更したのでMyClassのobserveValueForKeyPathが呼ばれる

以下のようにObserverを別クラスに分ける事もできます。

class MyClass: NSObject {
    dynamic var prop = 0
}

class MyObserver: NSObject {
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {

        print(keyPath) // Optional("prop")
        print(object) // Optional(<MyApp.MyClass: 0x7fdad1c013d0>)
        print(change) // Optional(["old": 0, "new": 10, "kind": 1])
    }
}

let observer = MyObserver()
let obj = MyClass()
obj.addObserver(observer, forKeyPath: "prop", options: [.New, .Old], context: nil)
obj.prop1 = 10
obj.removeObserver(observer, forKeyPath: "prop")

willSet/didSetとの違い

willSet/didSetとの違いとしては動的に監視を追加できる事があります。
動的に追加するので、サブクラスで追加されたプロパティーに対する監視も行う事ができます。

class MyClass: NSObject {
    override init() {
        super.init()
        propertyNames.forEach {
            addObserver(self, forKeyPath: $0, options: [.New, .Old], context: nil)
        }
    }

    deinit {
        propertyNames.forEach {
            removeObserver(self, forKeyPath: $0)
        }
    }

    var propertyNames: [String] {
        let mirror = Mirror(reflecting: self)
        return mirror.children.map { $0.label ?? "" }
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {

        print(keyPath)
        print(object)
        print(change)
    }
}

class SubClass: MyClass {
    dynamic var prop = 0
}

let obj = SubClass()
obj.prop = 10
62
58
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
62
58