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