2018月06日05日
SwiftでKVO(Key-Value Observing)
KVOはObjective-Cではときどき使っていましたが、Swiftでは使ったことがなかったので試してみます。
KVOは変数の値の変化を監視する仕組みです。
ViewControllerにボタンを配置して、ボタンを押した時に変数の値を変えて動作確認しました。
Hoge.swift
ViewControllerで監視する変数を持つクラスです。
import Foundation
class Hoge : NSObject {
@objc dynamic var count: Int = 0
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
var hoge: Hoge = Hoge()
override func viewDidLoad() {
super.viewDidLoad()
// countを監視対象にする
hoge.addObserver(self, forKeyPath: "count", options: [.old, .new], context: nil)
hoge.count = 10
hoge.count = 11
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
deinit {
// countを監視対象から削除
hoge.removeObserver(self, forKeyPath: "cout")
}
@IBAction func tapCountBugtton(sender: Any){
print("----- tapCountBugtton")
hoge.count += 1
print("hoge.count : \(hoge.count)")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("----- observeValue")
print("object : \(String(describing: object))")
print("keyPath : \(String(describing: keyPath))")
print("change : \(String(describing: change))")
}
}
ボタンをタップするとtapCountBugttonが実行されてhoge.countをインクリメントします。
出力結果
viewDidLoadでhoge.countに10を代入し、そのあとに11を代入しているのでobserveValueが2回呼ばれまています。
addObserverのoptionsで.oldと.newを指定しているのでchangeに新しい値と古い値が入っています。
----- observeValue
object : Optional(<KvoTest.Hoge: 0x600000016bc0>)
keyPath : Optional("count")
change : Optional([__C.NSKeyValueChangeKey(_rawValue: new): 10, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 0])
----- observeValue
object : Optional(<KvoTest.Hoge: 0x600000016bc0>)
keyPath : Optional("count")
change : Optional([__C.NSKeyValueChangeKey(_rawValue: new): 11, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 10])
ボタンをタップするとtapCountBugttonが実行され、hoge.countをインクリメントしています。
hoge.countの値が変わるのでobserveValueが呼ばれます。
----- tapCountBugtton
----- observeValue
object : Optional(<KvoTest.Hoge: 0x600000016bc0>)
keyPath : Optional("count")
change : Optional([__C.NSKeyValueChangeKey(_rawValue: new): 12, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 11])
hoge.count : 12
Objective-Cの時代からあったKVOなので簡単に試せると思ったのですが、うまく通知できずハマってしまいました。
最初、Hogeクラスのcountを下記のように定義していました。
これではKVOはうまく動きませんでした。
var count: Int = 0
下記のように@objc dynamicを指定すると正常に動作します。
@objc dynamic var count: Int = 0
監視対象の変数にはdynamicの指定が必要なようです。
Swift4から@objcを指定が必要なようです。