KVO(Key-Value Observingとは)
非同期処理を実装するときに、処理が終わってから別の処理を実行したい場合や処理の結果を受け取りたい時があると思います。
そのよな場合に、Key-Value Observer`(キー値監視、KVO)というオブジェクトのプロパティの変化を監視して検出できる仕組みを使うことができます。
基本的なKVOの例(非同期でわない)
class MyClass: NSObject { // NSObjectのサブクラス
dynamic var prop = 0 // 監視対象にはdynamicを付ける必要がある
override init() {
super.init()
//propプロパティを監視する
addObserver(self, forKeyPath: "prop", options: [.new, .old], context: nil)
}
deinit {
removeObserver(self, forKeyPath: "prop")
}
//propプロパティの値が変更された時(変更中)に呼ばれるメソッド
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print(keyPath) // Optional("prop")
print(object) // Optional(<__lldb_expr_32.MyClass: 0x608000026940>)
//propの値が変更された時に呼ばれたのでpropの値は変更した値になる
print((object! as AnyObject).prop) // 10
print(change) // Optional(["old": 0, "new": 10, "kind": 1])
}
}
<pre class="lang:swift decode:true " >【通知】**************isExecutingプロパティの値が変更されます(ワーカ側)
【呼び出し】------------------isExecutingプロパティの値が変更されobserveValueメソッド呼び出し(メイン側)
【通知】**************isExecutingプロパティの値が変更されました(ワーカ側)
【変更①】+++++++++++++++++isExecutingプロパティの値が変更されました(ワーカ側)
【通知】**************isExecutingプロパティの値が変更されます(ワーカ側)
【呼び出し】------------------isExecutingプロパティの値が変更されobserveValueメソッド呼び出し(メイン側)
【通知】**************isExecutingプロパティの値が変更されました(ワーカ側)
【変更②】+++++++++++++++++isExecutingプロパティの値が変更されました②(ワーカ側)
let obj = MyClass()
// <__lldb_expr_32.MyClass: 0x608000026940>
// propプロパティの値を変更したのでMyClassのobserveValueが呼ばれる
obj.prop = 10
監視プロパティとobserveValueを別クラスにした場合
class MyClass: NSObject {
dynamic var prop = 0
}
class MyObserver: NSObject {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print(keyPath) // Optional("prop")
print(object) // Optional(<__lldb_expr_32.MyClass: 0x608000026940>)
print((object! as AnyObject).prop) // 10
print(change) // Optional(["old": 0, "new": 10, "kind": 1])
}
}
let observer = MyObserver()
let myClass = MyClass()
myClass.addObserver(observer, forKeyPath: "prop", options: [.new, .old], context: nil)
myClass.prop = 10
非同期でのKVOの例
import Foundation
//---kvoを使ったOperationの実装--------------------------
class AsyncOperation: Operation {
//isExecutingは処理が実行中かどうかを返すプロパティ
//任意のタイミングで値の変更ができるようにオーバーライドして拡張(内容を変更)
override var isExecuting: Bool{
get{ return _isExecuting}
set(newVal){
//プロパティの値が変わることを(observeValueメソッドへ)通知
willChangeValue(forKey: "isExecuting")
print("【通知】**************isExecutingプロパティの値が変更されます(ワーカ側)")
_isExecuting = newVal
//プロパティの値が変わったことを(observeValueメソッドへ)通知
didChangeValue(forKey: "isExecuting")
print("【通知】**************isExecutingプロパティの値が変更されました(ワーカ側)")
}
}
private var _isExecuting = false
//処理を非同期で実行するかどうか
override var isAsynchronous: Bool{
get{ return true}
}
//処理が開始されると呼び出されるメソッド
//ここのワーカスレッドの処理を記述
//このメソッドをオーバーライドするとmainメソッドは実行されなくなる
override func start() {
//実行中を通知
self.isExecuting = true
print("【変更①】+++++++++++++++++isExecutingプロパティの値が変更されました(ワーカ側)")
//実行が終わったことを通知
self.isExecuting = false
print("【変更②】+++++++++++++++++isExecutingプロパティの値が変更されました②(ワーカ側)")
}
}
import UIKit
import Foundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//並列処理を実行させるためのOperationQueueオブジェクトを生成
//イニシャライザでの生成
let queue = OperationQueue()
let ope = AsyncOperation()
queue.addOperation(ope)
//opeオブジェクトのisExecutingプロパティを監視する
ope.addObserver(self, forKeyPath: "isExecuting", options: .new, context: nil)
}
//opeオブジェクトのisExecutingプロパティの値が変わった時に呼び出されるメソッド
//isExecutingプロパティの値が変わったタイミングで実行したい処理を書く
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("【呼び出し】------------------isExecutingプロパティの値が変更されobserveValueメソッド呼び出し(メイン側)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
実行結果
【通知】**************isExecutingプロパティの値が変更されます(ワーカ側)
【呼び出し】——————isExecutingプロパティの値が変更されobserveValueメソッド呼び出し(メイン側)
【通知】**************isExecutingプロパティの値が変更されました(ワーカ側)
【変更①】+++++++++++++++++isExecutingプロパティの値が変更されました(ワーカ側)
【通知】**************isExecutingプロパティの値が変更されます(ワーカ側)
【呼び出し】——————isExecutingプロパティの値が変更されobserveValueメソッド呼び出し(メイン側)
【通知】**************isExecutingプロパティの値が変更されました(ワーカ側)
【変更②】+++++++++++++++++isExecutingプロパティの値が変更されました②(ワーカ側)