KVO的底層實(shí)現(xiàn)原理

addObserver:forKeyPath:options:context:各個(gè)參數(shù)的作用分別是什么, observer中需要實(shí)現(xiàn)哪個(gè)方法才能獲得KVO回調(diào)?

/**
 1\. self.person:要監(jiān)聽的對(duì)象
 2\. 參數(shù)說明:
    * @param addObserver  觀察者,負(fù)責(zé)處理監(jiān)聽事件的對(duì)象
    * @param forKeyPath 要監(jiān)聽的屬性
    * @param  options 觀察的選項(xiàng)(觀察新、舊值,也可以都觀察)
    * @param context 上下文,用于傳遞數(shù)據(jù),可以利用上下文區(qū)分不同的監(jiān)聽
 */
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];

/**
 *  當(dāng)監(jiān)控的某個(gè)屬性的值改變了就會(huì)調(diào)用
 *
 *  @param keyPath 監(jiān)聽的屬性名
 *  @param object  屬性所屬的對(duì)象
 *  @param change  屬性的修改情況(屬性原來的值`oldValue`、屬性最新的值`newValue`)
 *  @param context 傳遞的上下文數(shù)據(jù),與監(jiān)聽的時(shí)候傳遞的一致,可以利用上下文區(qū)分不同的監(jiān)聽
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"%@對(duì)象的%@屬性改變了:%@", object, keyPath, change);
}


一、KVO (Key-Value Observing)

KVO 是 Objective-C 對(duì)觀察者模式(Observer Pattern)的實(shí)現(xiàn)。也是 Cocoa Binding 的基礎(chǔ)。當(dāng)被觀察對(duì)象的某個(gè)屬性發(fā)生更改時(shí),觀察者對(duì)象會(huì)獲得通知。

有意思的是,你不需要給被觀察的對(duì)象添加任何額外代碼,就能使用 KVO 。這是怎么做到的?

二、 KVO內(nèi)部實(shí)現(xià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á)到隱藏生成的派生類
image.png

三、如何手動(dòng)觸發(fā)一個(gè)value的KVO

  • 自動(dòng)觸發(fā)的場(chǎng)景:在注冊(cè)KVO之前設(shè)置一個(gè)初始值,注冊(cè)之后,設(shè)置一個(gè)不一樣的值,就可以觸發(fā)了
  • 想知道如何手動(dòng)觸發(fā),必須知道自動(dòng)觸發(fā) KVO 的原理,見上面的描述
  • 手動(dòng)觸發(fā)演示
@property (nonatomic, strong) NSDate *now;

- (void)viewDidLoad
{
    [super viewDidLoad];

    // “手動(dòng)觸發(fā)self.now的KVO”,必寫。
    [self willChangeValueForKey:@"now"];

    // “手動(dòng)觸發(fā)self.now的KVO”,必寫。
    [self didChangeValueForKey:@"now"];
}

四、補(bǔ)充: 如何關(guān)閉默認(rèn)的KVO的默認(rèn)實(shí)現(xiàn),并進(jìn)入自定義的KVO實(shí)現(xiàn)?(看鏈接)


五、附注: KVC底層實(shí)現(xiàn)原理(如下)

KVC運(yùn)用了一個(gè)isa-swizzling技術(shù). isa-swizzling就是類型混合指針機(jī)制, 將2個(gè)對(duì)象的isa指針互相調(diào)換, 就是俗稱的黑魔法.
KVC主要通過isa-swizzling, 來實(shí)現(xiàn)其內(nèi)部查找定位的. 默認(rèn)的實(shí)現(xiàn)方法?由NSOject提供isa指針, 如其名稱所指,(就是is a kind of的意思), 指向分發(fā)表對(duì)象的類. 該分發(fā)表實(shí)際上包含了指向?qū)崿F(xiàn)類中的方法的指針, 和其它數(shù)據(jù)。

  • 具體主要分為三大步
  • 第一步:尋找該屬性有沒有setsetter方法?有,就直接賦值
  • 第二步:尋找有沒有該屬性帶下劃線的成員屬性?有,就直接賦值
  • 第三步:尋找有沒有該屬性的成員屬性?有,就直接賦值
  • 或者這么說
  • 1、首先搜索setKey:方法.(key指成員變量名, 首字母大寫)
  • 2、上面的setter方法沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的順序搜索成員名.(NSKeyValueCodingCatogery中實(shí)現(xiàn)的類方法, 默認(rèn)實(shí)現(xiàn)為返回YES)
  • 3、如果沒有找到成員變量, 調(diào)用setValue:forUnderfinedKey:

比如說如下的一行KVC的代碼:

  • 舉個(gè)??e.g:

[object setValue:@"13123" forKey:@"uuid"];

就會(huì)被編譯器處理成:
// 首先找到對(duì)應(yīng)sel
SEL sel = sel_get_ uuid("setValue:forKey:");
// 根據(jù)object->isa找到sel對(duì)應(yīng)的IMP實(shí)現(xiàn)指針
IMP method = objc_msg_lookup (object->isa,sel);
// 調(diào)用指針完成KVC賦值
method(object, sel, @"13123", @"uuid");

可供參考文章
轉(zhuǎn)自大牛

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 引用孫源的話 http://blog.sunnyxx.com/2014/03/09/objc_kvo_secret...
    charlotte2018閱讀 477評(píng)論 0 1
  • 問題 iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?(KVO的本質(zhì)是什么?)如何取消系統(tǒng)默認(rèn)的 KVO 并自己手動(dòng)觸發(fā)?...
    6ffd6634d577閱讀 383評(píng)論 0 0
  • 主要是寫一下自己對(duì)如何自己動(dòng)手實(shí)現(xiàn) KVO這個(gè)文章的理解。 當(dāng)你觀察一個(gè)對(duì)象時(shí),一個(gè)新的類會(huì)動(dòng)態(tài)被創(chuàng)建。這個(gè)類繼承...
    三十一_iOS閱讀 913評(píng)論 0 0
  • KVO 是 OC 觀察者設(shè)計(jì)模式的一種KVO 的實(shí)現(xiàn)依賴于 OC 強(qiáng)大的 RuntimeKVO是Cocoa提供的...
    Onlyoner閱讀 2,311評(píng)論 3 4
  • 朦朧迷霧清寒煙,蘭若寺中自翩躚。 冷綠芭蕉幽月色,素紗縹緲黑林間。 黛眉輕蹙目噙淚,莞爾一笑百花醉。 細(xì)綰青絲蝴蝶...
    漫步菌閱讀 401評(píng)論 0 8

友情鏈接更多精彩內(nèi)容