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

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

KVO即Key-Value Observing,它提供一種機(jī)制,當(dāng)指定的對(duì)象的屬性被修改后,則對(duì)象就會(huì)接受到通知。簡單的說就是每次指定的被觀察的對(duì)象的屬性被修改后,KVO就會(huì)自動(dòng)通知相應(yīng)的觀察者了。

KVO的原理

  1. 當(dāng)一個(gè)object有觀察者時(shí),動(dòng)態(tài)創(chuàng)建這個(gè)object的類的子類
  2. 對(duì)于每個(gè)被觀察的property,重寫其set方法
  3. 在重寫的set方法中調(diào)用- willChangeValueForKey:和- didChangeValueForKey:通知觀察者
  4. 當(dāng)一個(gè)property沒有觀察者時(shí),刪除重寫的方法
  5. 當(dāng)沒有observer觀察任何一個(gè)property時(shí),刪除動(dòng)態(tài)創(chuàng)建的子類

當(dāng)我們?cè)谔砑佑^察者的時(shí)候,打一個(gè)斷點(diǎn),在設(shè)置屬性的時(shí)候打一個(gè)斷點(diǎn)

發(fā)現(xiàn)對(duì)象P的isa指針居然變量了,指向的類也不是我們自己創(chuàng)建的,那也就說明是系統(tǒng)自己創(chuàng)建的,Person類的子類NSKVONotifying_Person,并且重寫了set方法。其實(shí),就是去判斷有沒有調(diào)用一個(gè)對(duì)象的set方法

現(xiàn)在我們就手動(dòng)的模擬下這種情況

首先我們需要?jiǎng)?chuàng)建一個(gè)NSObject的分類,創(chuàng)建自己添加觀察者的方法

@interface NSObject (KVO)
- (void)xf_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end

#import "NSObject+KVO.h"
#import <objc/runtime.h>
#import "XFKVONotifying_Person.h"

@implementation NSObject (KVO)

- (void)xf_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {

    // 修改isa,改變當(dāng)前對(duì)象的類名
    object_setClass(self, [XFKVONotifying_Person class]);

    //添加關(guān)聯(lián),把觀察者保存到當(dāng)前對(duì)象里
    objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

@end

由于我們使用了運(yùn)行時(shí),所以別忘記導(dǎo)入 <objc/runtime.h>

我們還需要?jiǎng)?chuàng)建一個(gè) XFKVONotifying_Person類,并繼承于 Person

#import "Person.h"

@interface XFKVONotifying_Person : Person

@end

#import "XFKVONotifying_Person.h"
#import <objc/runtime.h>

@implementation XFKVONotifying_Person

-(void)setAge:(NSInteger)age {

    [super setAge:age];

    //取出觀察者,并通知觀察者屬性改變
    id observer = objc_getAssociatedObject(self, @"observer");
    
    [observer observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];

}

@end

最后我們調(diào)用自己寫的方法:

[p xf_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

一樣可以觀察到屬性值的變化

總結(jié)

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

  1. 動(dòng)態(tài)創(chuàng)建NSKVONotifying_Person,NSKVONotifying_Person是Person子類,做KVO
  2. 修改當(dāng)前對(duì)象的isa指針->NSKVONotifying_Person
  3. 只要調(diào)用對(duì)象的set,就會(huì)調(diào)用NSKVONotifying_Person的set方法
  4. 重寫NSKVONotifying_Person的set方法:
  • [super set:]
  • 通知觀察者,告訴你屬性改變
最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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