Close icon
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を指定が必要なようです。



アトトックラボとは

株式会社アトトックメンバー が技術の話、デザインの話、キャラクターの話、ときどき脱線してガジェットの話やライフハックの話など好きなことを書いています。


連載記事


最近の記事


タグ