MG--探究KVO的底層實現原理
willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey:各個引數的作用分別是什麼, observercolor{#00DD00}{observer}observer中需要實現哪個方法才能獲得KVOcolor{#dd0000}{KVO}KVO回撥
/**
* 1. self.person要監聽的物件
* 2. 引數說明
* @param addObserver 觀察者負責處理監聽事件的物件
* @param forKeyPath 要監聽的屬性
* @param options 觀察的選項觀察新、舊值也可以都觀察
* @param context 上下文用於傳遞資料可以利用上下文區分不同的監聽
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
* 當監控的某個屬性的值改變了就會呼叫
*
* @param keyPath 監聽的屬性名
* @param object 屬性所屬的物件
* @param change 屬性的修改情況屬性原來的值`oldValue`、屬性最新的值`newValue`
* @param context 傳遞的上下文資料與監聽的時候傳遞的一致可以利用上下文區分不同的監聽
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@物件的%@屬性改變了%@", object, keyPath, change);
}
一、KVO(Key−ValueObserving)color{#dd0000}{KVO(Key-Value Observing)}KVO(Key−ValueObserving)
KVOcolor{#dd0000}{KVO}KVO 是 Objective-C 對觀察者模式Observer Pattern的實現。也是 Cocoa Binding 的基礎。當被觀察物件的某個屬性發生更改時觀察者物件會獲得通知。
有意思的是你不需要給被觀察的物件新增任何額外程式碼就能使用 KVOcolor{#dd0000}{KVO}KVO 。這是怎麼做到的
二、 KVOcolor{#dd0000}{KVO}KVO內部實現原理
- KVOcolor{#dd0000}{KVO}KVO是基於runtime機制實現的
- 當某個類的屬性物件
第一次被觀察
時系統就會在執行期動態
地建立該類的一個派生類
在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內實現真正的通知機制
- 如果原類為Person那麼生成的派生類名為NSKVONotifyingPersoncolor{#00DD00}{NSKVONotifying_Person}NSKVONotifyingPerson
- 每個類物件中都有一個isa指標指向當前類當一個類物件的第一次被觀察那麼系統會偷偷將isa指標指向動態生成的派生類從而在給被監控屬性賦值時執行的是派生類的setter方法
- 鍵值觀察通知依賴於NSObject 的兩個方法: willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:在一個被觀察屬性發生改變之前 willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey:一定會被呼叫這就 會記錄舊的值。而當改變發生後didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:會被呼叫繼而 observeValueForKey:ofObject:change:context:color{#00DD00}{observeValueForKey:ofObject:change:context:}observeValueForKey:ofObject:change:context:也會被呼叫。
- 補充KVOcolor{#dd0000}{KVO}KVO的這套實現機制中蘋果還偷偷重寫了class方法讓我們誤認為還是使用的當前類從而達到隱藏生成的派生類
三、如何手動觸發一個value的KVOcolor{#dd0000}{KVO}KVO
- 自動觸發的場景在註冊KVO之前設定一個初始值註冊之後設定一個不一樣的值就可以觸發了
- 想知道如何手動觸發必須知道自動觸發 KVO 的原理見上面的描述
- 手動觸發演示
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// “手動觸發self.now的KVO”必寫。
[self willChangeValueForKey:@"now"];
// “手動觸發self.now的KVO”必寫。
[self didChangeValueForKey:@"now"];
}
四、補充 如何關閉預設的KVOcolor{#dd0000}{KVO}KVO的預設實現並進入自定義的KVOcolor{#dd0000}{KVO}KVO實現看連結
五、附註: KVCcolor{#0000FF}{KVC}KVC底層實現原理(如下)
KVOcolor{#dd0000}{KVO}KVO的基礎是KVCcolor{#0000FF}{KVC}KVCKVCcolor{#0000FF}color{#0000FF}{KVC}KVC運用了一個isa-swizzling技術. isa-swizzling就是型別混合指標機制, 將2個物件的isa指標互相調換, 就是俗稱的黑魔法.
KVCcolor{#0000FF}{KVC}KVC主要透過isa-swizzling, 來實現其內部查詢定位的. 預設的實現方法由NSOject提供isa指標, 如其名稱所指,(就是is a kind of的意思), 指向分發表物件的類. 該分發表實際上包含了指向實現類中的方法的指標, 和其它資料。
- 具體主要分為三大步
第一步尋找該屬性有沒有setsetter方法有就直接賦值
第二步尋找有沒有該屬性帶下劃線的成員屬性有就直接賦值
第三步尋找有沒有該屬性的成員屬性有就直接賦值
- 或者這麼說
1、首先搜尋setKey:方法.(key指成員變數名, 首字母大寫)
2、上面的setter方法沒找到, 如果類方法accessInstanceVariablesDirectly返回YES. 那麼按 _key, _isKeykey, iskey的順序搜尋成員名.(NSKeyValueCodingCatogery中實現的類方法, 預設實現為返回YES)
3、如果沒有找到成員變數, 呼叫setValue:forUnderfinedKey:
比如說如下的一行KVC的程式碼
-
舉個e.g:
[object setValue:@"13123" forKey:@"uuid"];
就會被編譯器處理成:
// 首先找到對應sel
SEL sel = sel_get_ uuid("setValue:forKey:");
// 根據object->isa找到sel對應的IMP實現指標
IMP method = objc_msg_lookup (object->isa,sel);
// 呼叫指標完成KVC賦值
method(object, sel, @"13123", @"uuid");
-
可供參考文章
面試題
透過修改類的成員變數不會觸發KVOcolor{#dd0000}{KVO}KVO,那為什麼透過KVCcolor{#0000FF}{KVC}KVC的setValue:forkey: 給成員變數賦值會觸發KVO呢?
首先KVOcolor{#dd0000}{KVO}KVO的基礎是KVCcolor{#0000FF}{KVC}KVC我們看下KVCcolor{#0000FF}{KVC}KVC的底層實現
1、首先搜尋setKey:方法.(key指成員變數名, 首字母大寫)
2、上面的setter方法沒找到, 如果類方法accessInstanceVariablesDirectlycolor{#00DD00}{accessInstanceVariablesDirectly}accessInstanceVariablesDirectly返回YES. 那麼按 _key, _isKeykey, iskey的順序搜尋成員名。
如果找到了就會觸發KVO因為底層內部會呼叫willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。
你可以重寫該類的呼叫willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法去驗證當KVC改變屬性值的時候比如Person繼承NSObject它有一個成員變數@public int _age;[self.person1 setValue:@(10) forKey:@“age”]; 會呼叫willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。所以會觸發KVO。
透過修改類的成員變數不會觸發KVO因為成員變數不會生成setter方法直接訪問成員變數自然不會觸發KVO而要觸發KVO本質是必須呼叫呼叫willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey: 和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。KVO的底層實現也是透過重寫setter方法 setter方法裡面呼叫willChangeValueForKey:color{#00DD00}{willChangeValueForKey:}willChangeValueForKey:和 didChangeValueForKey:color{#00DD00}{didChangeValueForKey:}didChangeValueForKey:方法。
-
輕輕點選關注我
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4550/viewspace-2822616/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- KVO的使用和底層實現原理
- iOS窺探KVO底層實現原理篇iOS
- OC底層探索(十六) KVO底層原理
- RunLoop底層原理探究OOP
- 底層原理探究(二)RunLoopOOP
- iOS底層原理探究-RunloopiOSOOP
- iOS窺探KVO底層實現實戰篇iOS
- iOS底層原理探究-RuntimeiOS
- iOS底層原理總結篇-- 深入理解 KVC\KVO 實現機制iOS
- ArrayList底層的實現原理
- HashMap底層實現原理HashMap
- NSDictionary底層實現原理
- AutoreleasePool底層實現原理
- iOS底層原理 - Block本質探究iOSBloC
- iOS開發·KVO用法,原理與底層實現: runtime模擬實現KVO監聽機制(Blcok及Delgate方式)iOS
- MySQL Join的底層實現原理MySql
- iOS底層原理總結 - 探尋KVO本質iOS
- MySQL索引底層實現原理MySql索引
- (iOS)KVO 的實現原理iOS
- 刨根問底KVO原理
- KVO使用及實現原理
- php底層原理之陣列實現PHP陣列
- javascript事件機制底層實現原理JavaScript事件
- 面試官:說說反射的底層實現原理?面試反射
- iOS底層-KVC使用實踐以及實現原理iOS
- iOS底層原理總結 - 關聯物件實現原理iOS物件
- iOS底層原理總結 -- 利用Runtime原始碼 分析Category的底層實現iOS原始碼Go
- React-Router底層原理分析與實現React
- 面試必問:HashMap 底層實現原理分析面試HashMap
- iOS底層原理探究- NSObject 所佔記憶體iOSObject記憶體
- 筆記-集合NSSet、字典NSDictionary的底層實現原理筆記
- 探究JS V8引擎下的“陣列”底層實現JS陣列
- iOS底層學習 - 記憶體管理之weak原理探究iOS記憶體
- 探究synchronized底層原理(基於JAVA8原始碼分析)synchronizedJava原始碼
- HashMap的實現原理 HashMap底層實現,hashCode如何對應bucket?HashMap
- [iOS]用程式碼探究 KVO 原理(真原創)iOS
- 深入理解Java中的底層阻塞原理及實現Java
- HashMap的底層原理HashMap