Key-Value Coding: 鍵值編碼 (KVC)
方法調用:
// 對象屬性 // 類似: Person -> name setValue: forKey: // 對象的屬性或者 屬性的屬性...... 可見它已經包含前者. // 類似: Person -> car -> name setValue: forKeyPath:
KVC運用了一個isa-swizzling技術. isa-swizzling就是類型混合指針機制, 將2個對象的isa指針互相調換, 就是俗稱的黑魔法.
KVC主要通過isa-swizzling, 來實現其內部查找定位的. 默認的實現方法由NSOject提供
isa指針, 如其名稱所指,(就是is a kind of的意思), 指向分發表對象的類. 該分發表實際上包含了指向實現類中的方法的指針, 和其它數據。
比如說如下的一行KVC的代碼:
[object setValue:@"134567" forKey:@"uid"]; 就會被編譯器處理成: // 首先找到對應sel SEL sel = sel_get_uid("setValue:forKey:"); // 根據object->isa找到sel對應的IMP實現指針 IMP method = objc_msg_lookup (object->isa,sel); // 調用指針完成KVC賦值 method(object, sel, @"134567", @"uid");
KVC鍵值查找原理
setValue:forKey:搜索方式
1、首先搜索setKey:方法.(key指成員變量名, 首字母大寫) 2、上面的setter方法沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey,key, iskey的順序搜索成員名.(NSKeyValueCodingCatogery中實現的類方法, 默認實現為返回YES) 3、如果沒有找到成員變量, 調用setValue:forUnderfinedKey:
valueForKey:的搜索方式
1、首先按getKey, key, isKey的順序查找getter方法, 找到直接調用. 如果是BOOL、int等內建值類型, 會做NSNumber的轉換. 2、上面的getter沒找到, 查找countOfKey, objectInKeyAtindex, KeyAtindexes格式的方法. 如果countOfKey和另外兩個方法中的一個找到, 那么就會返回一個可以響應NSArray所有方法的代理集合的NSArray消息方法. 3、還沒找到, 查找countOfKey, enumeratorOfKey, memberOfKey格式的方法. 如果這三個方法都找到, 那么就返回一個可以響應NSSet所有方法的代理集合. 4、還是沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那么按 _key, _isKey, key, iskey的順序搜索成員名. 5、再沒找到, 調用valueForUndefinedKey.
Key-Value Observing 鍵值觀察(KVO), KVO是觀察者模式的一種應用
觀察者思想:
一個目標對象管理所有依賴於它的觀察者對象,並在它自身的狀態改變時主動通知觀察者對象。這個主動通知通常是通過調用各觀察者對象所提供的接口方法來實現的。觀察者模式較完美地將目標對象與觀察者對象解耦。
方法調用
// 添加一個觀察者 [self.object addObserver:self forKeyPath:@"uid" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil]; // 觀察者監聽到之后回調方法 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { NSLog(@"keyPath: %@", keyPath); NSLog(@"object: %@", object); NSLog(@"change: %@", change); NSLog(@"context: %@", context); } // 移除觀察者, 類在銷毀前需要銷毀 [self.object removeObserver:self forKeyPath:@"uid"];
由此可見, 只有當我們調用KVC去訪問key值的時候KVO才會起作用. 所以肯定確定的是, KVO是基於KVC實現的,
系統實現KVO有以下幾個步驟:
- 當類A的對象第一次被觀察的時候,系統會利用運行時動態創建與類A一樣的NSKVONotifying_A派生類, 並且只有1個屬性的get, set方法實現.
- 在派生類NSKVONotifying_A中重寫類A的setter方法,NSKVONotifying_A類在被重寫的setter方法中實現通知機制。
- 類NSKVONotifying_A重寫會 class方法,將自己偽裝成類A。類NSKVONotifying_A還會重寫dealloc方法釋放資源。
- 系統將所有指向類A對象的isa指針指向類NSKVONotifying_A的對象。
下面就是在調用KVO過程中object對象的isa指針指向
在沒有添加觀察者之前 isa 指向的是Student類
在添加完觀察者之后, isa指針指向了NSKVONotifying_Student類, 所以能肯定NSKVONotifying_Student 是后來被創建出來作為監聽用的