1、KVO<Key-Value-Observing>
顧名思義,鍵值監聽,可以用于監聽某個物件屬性值的變化,KVO是一個非正式協議,提供了一個途徑,使物件(觀察者)能夠觀察其他物件(被觀察者)的屬性,當被觀察者的屬性發生變化時,觀察者就會被告知該變化,首先了解一下KVO的基本使用,然后在此基礎上,我們深入了解一下KVO的底層實作原理,
//給一個物件屬性添加KVO監聽
[self addObserver:(nonnull NSObject *)
forKeyPath:(nonnull NSString *)
options:(NSKeyValueObservingOptions)
context:(nullable void *)]
//當監聽物件的屬性值發生改變時,就會呼叫
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context
//需要在不使用的時候,移除監聽
- (void)dealloc{
[self removeObserver:self forKeyPath:@""];
}
2、KVO的底層實作原理
在了解KVO底層之前,我們先要對isa一些基本的概念有個了解,instance物件的isa指向class物件,class物件的isa指向Meta-Class物件,Meta-Class物件的isa指向基類的Meta-Class物件,如果不了解的可以去看一下我的另一篇文章《通俗易懂的ios方法呼叫底層原理》,
KVO 是基于Runtime機制實作的,KVO是運用了一個isa-swizzling 技術,就是型別混合指標機制, 將2個物件的isa指標互相調換, 就是俗稱的黑魔法.
當某個類的屬性物件
第一次被觀察時,系統就會在運行期動態地創建該類的一個派生類,在這個派生類中重寫基類中被觀察屬性的setter 方法,派生類在被重寫的setter方法內實作真正的通知機制如果原類為Person,那么生成的派生類名為
NSKVONotifying_Person每個類物件中都有一個isa指標指向當前類,當一個類物件的第一次被觀察,那么系統會偷偷將isa指標指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法
呼叫非監聽屬性設定方法,會通過
NSKVONotify_Person的superclass,找到Person類物件,再呼叫其 實體方法
鍵值觀察通知依賴于NSObject 的兩個方法:
willChangeValueForKey:和didChangevlueForKey:;在一個被觀察屬性發生改變之前,willChangeValueForKey:一定會被呼叫,這就 會記錄舊的值,而當改變發生后,didChangeValueForKey:會被呼叫,繼而observeValueForKey:ofObject:change:context:也會被呼叫,補充:KV0的這套實作機制中蘋果還偷偷重寫了class方法,讓我們誤認為還是使用的當前類,從而達到隱藏生成的派生類,如果沒有重寫
class方法,當該物件呼叫class方法時,會在自己的方法快取串列,方法串列,父類快取,方法串列一直向上去查找該方法,因為class方法是NSObject中的方法,如果不重寫最終可能會回傳NSKVONotifying_Person,就會將該類暴露出來,也給開發者造成困擾,寫的是Person,添加KVO之后class方法回傳怎么是另一個類,
3、kvo和 notification(通知)的區別?
KVO和NSNotificationCenter都是iOS中觀察者模式的一種實作,區別在于,相對于被觀察者和觀察者之間的關系,KVO是一對一的,而不是一對多的,KVO對被監聽物件無侵入性,不需要修改其內部代碼即可實作監聽,notification 的優點是監聽不局限于屬性的變化,還可以對多種多樣的狀態變化進行監聽,監聽范圍廣,而且可以一對多,
4、測驗代碼
NSKeyValueObservingOptions option = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
Person *person1 = [Person new];
NSLog(@"person1添加KVO監聽物件之前-類物件 -%@", object_getClass(person1));
NSLog(@"person1添加KVO監聽之前-方法實作 -%p", [person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person1添加KVO監聽之前-元類物件 -%@", object_getClass(object_getClass(person1)));
[person1 addObserver:self forKeyPath:@"age" options:option context:@"age chage"];
NSLog(@"person1添加KVO監聽物件之后-類物件 -%@", object_getClass(person1));
NSLog(@"person1添加KVO監聽之后-方法實作 -%p", [person1 methodForSelector:@selector(setAge:)]);
NSLog(@"person1添加KVO監聽之后-元類物件 -%@", object_getClass(object_getClass(person1)));
//列印結果
KVO-test[1214:513029] person1添加KVO監聽物件之前-類物件 -Person
KVO-test[1214:513029] person1添加KVO監聽之前-方法實作 -0x100411470
KVO-test[1214:513029] person1添加KVO監聽之前-元類物件 -Person
KVO-test[1214:513029] person1添加KVO監聽物件之后-類物件 -NSKVONotifying_Person
KVO-test[1214:513029] person1添加KVO監聽之后-方法實作 -0x10076c844
KVO-test[1214:513029] person1添加KVO監聽之后-元類物件 -NSKVONotifying_Person
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/265945.html
標籤:其他
上一篇:build.gradle 代碼出錯 有沒有大神可以幫忙解決一下 這里面代碼有些是灰色
下一篇:lex1.yy.c(12) : fatal error C1083: 無法打開包括檔案:“stdio.h” :No such file or directory
