KVC、KVO實現原理

1、KVC 運用了一個isa-swizzling技術。isa-swizzling就是類型混合指針機制。KVC主要經過isa-swizzling,來實現其內部查找定位的。isa指針,如其名稱所指,(就是is a kind of的意思),指向維護分發表的對象的類。該分發表實際上包含了指向實現類中的方法的指針,和其它數據。
函數

    好比說以下的一行KVC的代碼:ui

[site setValue:@"sitename" forKey:@"name"];
    就會被編譯器處理成:
spa

SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (site->isa,sel);
method(site, sel, @"sitename", @"name");
    首先介紹兩個基本概念:
指針

    (1)SEL數據類型:它是編譯器運行Objective-C裏的方法的環境參數。code

    (2)IMP數據類型:他其實就是一個編譯器內部實現時候的函數指針。當Objective-C編譯器去處理實現一個方法的時候,就會指向一個IMP對象,這個對象是C語言表述的類型。orm


    KVC 再某種程度上提供了訪問器的替代方案。不過訪問器方法是一個很好的東西,以致於只要是有可能,KVC也儘可能再訪問器方法的幫助下工做。爲了設置或者返回對象屬性,KVC按順序使用以下技術:
server

1)檢查是否存在名爲-set<key>:的方法,並使用它作設置值。對於-get<key>和-set<key>:方法,將大寫Key字符串的第一個字母,並與Cocoa的方法命名保持一致;對象

2)若是上述方法不可用,則檢查名爲-_<key>、-_is<key>(只針對布爾值有效)、-_get<key>和-_set<key>:方法;rem

3)若是沒有找到訪問器方法,能夠嘗試直接訪問實例變量。實例變量能夠是名爲:<key>或_<key>;字符串

4)若是仍爲找到,則調用valueForUndefinedKey:和setValue:forUndefinedKey:方法。這些方法的默認實現都是拋出異常,咱們能夠根據須要重寫它們。


2、KVO 是基於KVC實現的,看下面的代碼:

#pragma mark - KVO實現原理
    
    Person *person = [[Person alloc] init];
    
    [person setName:@"Jacedy"];
    
    // 設置監聽
    [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    
    [person setName:@"Jack"];
    
    self.person = person;
}

// 響應監聽
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%@ 對象的 %@ 屬性改變了:%@", object, keyPath, change);
}

- (void)dealloc
{
    // 移除監聽
    [self.person removeObserver:self forKeyPath:@"name"];
}

對代碼進行斷點跟蹤發現以下:

當添加了監聽後:

不難發現,person對象的isa指針由Person變成了NSKVONotifying_Perosn。其實,當某個類的對象第一次被觀察時,系統就會在運行期動態地建立該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。派生類在被重寫的 setter 方法實現真正的通知機制:

- (void)setName:(NSString *)name
{
    [super setName:name];
    
    [監聽器 observeValueForKeyPath:@"name"  ofObject:self  change:@{}  context:nil];
}
相關文章
相關標籤/搜索