iOS 底層解析-----KVO與KVC原理

上文鏈接從isa指針看繼承關(guān)系

KVO 全稱(chēng)Key-Value Observing,俗稱(chēng)"鍵值監(jiān)聽(tīng)",可以用于監(jiān)聽(tīng)某個(gè)對(duì)象屬性值的改變

KVO 基本使用方法

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    self.person1.height = 11;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    self.person2.height = 22;
    
    // 給person1對(duì)象添加KVO監(jiān)聽(tīng)
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:@"456"];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person1.age = 20;
    self.person2.age = 20;
    
    self.person1.height = 30;
    self.person2.height = 30;
}

// 當(dāng)監(jiān)聽(tīng)對(duì)象的屬性值發(fā)生改變時(shí),就會(huì)調(diào)用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"監(jiān)聽(tīng)到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context);
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person1 removeObserver:self forKeyPath:@"height"];
}

從上述代碼可看出,alloc出來(lái)的person1 與 person2 實(shí)例對(duì)象,在點(diǎn)擊屏幕的時(shí)候,只監(jiān)測(cè)到了person1對(duì)象值的變化,沒(méi)有監(jiān)測(cè)到person2對(duì)象的變化?從代碼的角度考慮是因?yàn)閜erson2對(duì)象沒(méi)有添加KVO,那么添加了KVO之后,person1實(shí)例對(duì)象在內(nèi)存中發(fā)生了什么變換?

先看person2實(shí)例對(duì)象的內(nèi)存調(diào)用圖


未使用KVO監(jiān)聽(tīng).png

person1實(shí)例對(duì)象使用了KVO 那么它的內(nèi)存調(diào)用圖如下

使用KVO監(jiān)聽(tīng).png

OC在運(yùn)行時(shí)的時(shí)候自動(dòng)生成了一個(gè)NSKVONotifying_MJPerson 類(lèi)對(duì)象作為MJPerson類(lèi)對(duì)象的子類(lèi)對(duì)象,通過(guò)底層C _NSSetIntValueAndNotify方法 重寫(xiě)了setAge:方法,進(jìn)而實(shí)現(xiàn)KVO的過(guò)程

以下可理解為實(shí)現(xiàn)該過(guò)程的偽代碼

#import "MJPerson.h"

@interface NSKVONotifying_MJPerson : MJPerson

@end


#import "NSKVONotifying_MJPerson.h"

@implementation NSKVONotifying_MJPerson

- (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 偽代碼
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    // 通知監(jiān)聽(tīng)器,某某屬性值發(fā)生了改變
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

@end

總結(jié)如圖所示


調(diào)用過(guò)程總結(jié).png

從上圖可以看出來(lái)如果添加了KVO又不想修改屬性值,那怎么主動(dòng)調(diào)用監(jiān)聽(tīng)方法呢,即可以調(diào)用以下兩個(gè)方法主動(dòng)去觸發(fā)監(jiān)聽(tīng)方法

[self willChangeValueForKey:@"age"];

[self didChangeValueForKey:@"age"];

KVC 全稱(chēng)Key-Value Coding,俗稱(chēng)"鍵值編碼",可以通過(guò)一個(gè)Key來(lái)訪(fǎng)問(wèn)某個(gè)屬性

KVC常用方法 (經(jīng)常用到,用法不做過(guò)多解釋)

//賦值
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath; 
-(void)setValue:(id)value forKey:(NSString *)key;

//取值
-(id)valueForKeyPath:(NSSting *)keyPath;
-(id)valueForKey:(NSSting *)key;

直接展示原理圖

賦值邏輯

賦值.png

取值邏輯

取值.png

注意:賦值,或 取值 都是有查找順序的

問(wèn)題:KVC 修改屬性會(huì)觸發(fā)KVO么?
會(huì)觸發(fā)KVO的
因?yàn)镵VC內(nèi)部會(huì)實(shí)現(xiàn)willChangeValueForKey: didChangeValueForKey: 方法 具體筆記不再論證

下篇文章鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。