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]; }