什麼是觀察者模式?
觀察者模式本質上時一種釋出-訂閱模型,我們先打個比方 比如你追星,你想知道你喜歡的愛豆的最新動態, 所以你會關注她的微博。 一旦她發了新微博,微博就會推送告訴你。 然後你就可以及時看到愛豆的微博動態。 這就是觀察者模式,A對B的變化感興趣,就註冊為B的觀察者,當B發生變化時通知A,告知B發生了變化。
觀察者模式由三個角色組成:
觀察者
被觀察者
接收者
複製程式碼
當開始觀察的時候,觀察者就需要盯著被觀察者,當被觀察者發生變化的時候,都需要反饋訊息給接收者。在 iOS 中,系統已經實現了 KVO 和通知兩種觀察者模式。
觀察者模式用在什麼地方
當一個物件發生改變的時候,需要將它的變化進行反饋
複製程式碼
當開始監聽的時候,就需要把所有的改變反饋,直到取消監聽。
觀察者模式的使用
建立被觀察者類,實現增加監聽、移除監聽的功能
建立觀察者類,實現被觀察者監聽前後監聽後的值,並反饋給接受這
接受者接受監聽值。
複製程式碼
用法
雖然用法很基礎,還是簡單提一下。 新增觀察
[_obj addObserver:self forKeyPath:@"aName" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:(__bridge void *)(self)];
複製程式碼
方法的呼叫者(就是這個_obj)就是被觀察的物件。
observer引數是回撥的接收者。
keyPath是一個尋找路徑,最終落腳點是一個有效的可觀察屬性。
options有幾個配置回撥可選項,NSKeyValueObservingOptionOld表示獲取舊值,NSKeyValueObservingOptionNew表示獲取新值,NSKeyValueObservingOptionInitial表示在新增觀察的時候就立馬響應一個回撥,NSKeyValueObservingOptionPrior表示在被觀察屬性變化前後都回撥一次。
context是一個指標,用來準確匹配新增觀察和接收觀察,主要是在特定情況下無法區分該觀察回撥是否需要處理進行精確判斷。
複製程式碼
獲取回撥
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ((__bridge id)context == self) {
NSLog(@"keyPath: %@, object: %@, change: %@, context: %@", keyPath, object, change, context);
}
}
複製程式碼
變換一個屬性的值,列印如下:
keyPath: aName, object: <TestObj: 0x604000435340>, change: {
kind = 1;
new = jack;
old = "<null>";
}, context: <KVOVC: 0x7fb06cd3cd10>
複製程式碼
change字典裡面的new和old就是我們需要的值了,kind是關鍵路徑屬性的型別標識,具體可以去看api。 移除觀察
[_obj removeObserver:self forKeyPath:@"aName"];
複製程式碼
移除觀察很簡單,和移除通知比較類似,我們需要在不用繼續觀察的時候移除它,比如在控制器的dealloc方法裡面釋放,值得注意的是重複移除會 crash。 KVO 設計的槽點
其實作為開發者,大家應該經常聽到對 KVO 的吐槽:
回撥方式單一。
keypath設計容易寫錯。
KVO 的回撥有一個傳遞鏈,子類若不呼叫父類方法,傳遞鏈會中斷,這個設計感覺有些繁瑣。
多次移除同一 KVO 會 crash。
......
複製程式碼