KVO全稱為Key Value Observing,鍵值監(jiān)聽機(jī)制灼卢,由NSKeyValueObserving協(xié)議提供支持,NSObject類繼承了該協(xié)議,所以NSObject的子類都可使用該方法受扳。
KVO實(shí)現(xiàn)步驟
1.注冊(cè)觀察者(為被觀察這指定觀察者以及被觀察者屬性)
/*
options: 有4個(gè)值,分別是:
NSKeyValueObservingOptionOld 把更改之前的值提供給處理方法
NSKeyValueObservingOptionNew 把更改之后的值提供給處理方法
NSKeyValueObservingOptionInitial 把初始化的值提供給處理方法兔跌,一旦注冊(cè)勘高,立馬就會(huì)調(diào)用一次。通常它會(huì)帶有新值坟桅,而不會(huì)帶有舊值华望。
NSKeyValueObservingOptionPrior 分2次調(diào)用。在值改變之前和值改變之后仅乓。
*/
//注冊(cè)一個(gè)監(jiān)聽器用于監(jiān)聽指定的key路徑
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
2.實(shí)現(xiàn)回調(diào)方法
//當(dāng)key路徑對(duì)應(yīng)的屬性值發(fā)生改變時(shí)赖舟,監(jiān)聽器就會(huì)回調(diào)自身的監(jiān)聽方法,如下
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)contex
}
3.觸發(fā)回調(diào)方法
注意:路徑keyPath請(qǐng)參考上一篇KVO中的說明
4.移除觀察者
//刪除指定的key路徑監(jiān)聽器
[self.person removeObserver:self forKeyPath:@"name"];
//刪除指定的key路徑監(jiān)聽器夸楣,只是多了context參數(shù)
[self.person removeObserver:self forKeyPath:@"name" context:nil];
KVO的主要應(yīng)用場(chǎng)景
應(yīng)用場(chǎng)景:當(dāng)數(shù)據(jù)模型的數(shù)據(jù)發(fā)生改變時(shí)宾抓,視圖組件能動(dòng)態(tài)的更新,及時(shí)顯示數(shù)據(jù)模型更新后的數(shù)據(jù)豫喧。
比如:監(jiān)聽scrollView的contentOffset屬性石洗,來完成用戶滾動(dòng)時(shí)動(dòng)態(tài)改變某些控件的屬性實(shí)現(xiàn)效果,包括漸變導(dǎo)航欄紧显、下拉刷新控件等效果讲衫。
KVO的實(shí)現(xiàn)原理簡(jiǎn)要說明
KVO 是基于 runtime 機(jī)制實(shí)現(xiàn)的 當(dāng)某個(gè)類的屬性對(duì)象第一次被觀察時(shí),系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一 個(gè)派生類孵班,在這個(gè)派生類中重寫基類中任何被觀察屬性的 setter 方法涉兽。派生類在 被重寫的 setter 方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制。
如果原類為 Person重父,那么生成的派生類名為 NSKVONotifying_Person 每個(gè)類對(duì)象中都有一個(gè) isa 指針指向當(dāng)前類花椭,當(dāng)一個(gè)類對(duì)象的第一次被觀察,那么 系統(tǒng)會(huì)偷偷將 isa 指針指向動(dòng)態(tài)生成的派生類房午,從而在給被監(jiān)控屬性賦值時(shí)執(zhí)行的是派生類的 setter 方法矿辽。
鍵值觀察通知依賴于 NSObject 的兩個(gè)方法willChangeValueForKey:didChangevlueForKey:;在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey: 一定會(huì)被調(diào)用郭厌,這就 會(huì)記錄舊的值袋倔。而當(dāng)改變發(fā)生后,didChangeValueForKey: 會(huì)被調(diào)用折柠,繼而observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用宾娜。
補(bǔ)充:KVO 的這套實(shí)現(xiàn)機(jī)制中蘋果還偷偷重寫了 class 方法,讓我們誤認(rèn)為還是使用的當(dāng)前類扇售,從而達(dá)到隱藏生成的派生類前塔。