KVO實(shí)現(xiàn)原理
一,概述
KVO,Key-value Observing,它提供一種機(jī)制,當(dāng)制定的對象的屬性被修改后,其觀察者就會(huì)接收到通知,簡單的說就是每次指定的被觀察的對象的屬性被修改后,KCO就會(huì)自動(dòng)通知相應(yīng)的觀察者了。
KVO其實(shí)也是觀察者模式的一種應(yīng)用,這種模式有利于兩個(gè)類之間的解耦合。
二/KVO的具體實(shí)現(xiàn)
@interface ViewController ()
@property (strong, nonatomic) Person *p1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1.什么是通知
// 3個(gè)對象
self.p1 = [[Person alloc] init];
self.p1.name = @"p1";
//打印監(jiān)聽前類信息
[p1 printInfo];
// KVO是監(jiān)聽對象的屬性值的改變的
[self.p1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
self.p1.name = @"123";
//打印監(jiān)聽后類信息
[p1 printInfo];
[p1 removeObserver:self forKeyPath:@"name"];
//打印移除監(jiān)聽后類信息 [p1 printInfo]; }
// 這個(gè)方法時(shí)屬于 NSObject 類的,任何對象都可以作為觀察者
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { NSLog(@"監(jiān)聽到了%@的%@屬性發(fā)生了改變", object, keyPath); NSLog(@"%@", change); }
@end
person類方法:
-(void)printInfo {
NSLog(@"isa:%@, supper class:%@", NSStringFromClass(object_getClass(self)), class_getSuperclass(object_getClass(self)));
NSLog(@"self:%@, [self superclass]:%@", self, [self superclass]);
NSLog(@"age setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setAge:)));
NSLog(@"name setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setName:)));
NSLog(@"printInfo function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(printInfo))); }
三、KVO的實(shí)現(xiàn)原理
KVO 是基于運(yùn)行時(shí)實(shí)現(xiàn)的 isa Class NSKVONotifying_Person
基本的原理:當(dāng)觀察某對象A時(shí),KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)對象A當(dāng)前類的子類,并為這個(gè)新的子類重寫了被觀察屬性keyPath的setter 方法。setter 方法隨后負(fù)責(zé)通知觀察對象屬性的改變狀況。
深入刨析
Apple 使用了 isa 混寫(isa-swizzling)來實(shí)現(xiàn) KVO 。當(dāng)觀察對象A時(shí),KVO機(jī)制動(dòng)態(tài)創(chuàng)建一個(gè)新的名為: NSKVONotifying_A的新類,該類繼承自對象A的本類,且KVO為NSKVONotifying_A重寫觀察屬性的setter 方法,setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察對象屬性值的更改情況。(備注: isa 混寫(isa-swizzling)isa:is a kind of ; swizzling:混合,攪合;)
①NSKVONotifying_A類剖析:在這個(gè)過程,被觀察對象的 isa 指針從指向原來的A類,被KVO機(jī)制修改為指向系統(tǒng)新創(chuàng)建的子類 NSKVONotifying_A類,來實(shí)現(xiàn)當(dāng)前類屬性值改變的監(jiān)聽;
所以當(dāng)我們從應(yīng)用層面上看來,完全沒有意識(shí)到有新的類出現(xiàn),這是系統(tǒng)“隱瞞”了對KVO的底層實(shí)現(xiàn)過程,讓我們誤以為還是原來的類。但是此時(shí)如果我們創(chuàng)建一個(gè)新的名為“NSKVONotifying_A”的類(),就會(huì)發(fā)現(xiàn)系統(tǒng)運(yùn)行到注冊KVO的那段代碼時(shí)程序就崩潰,因?yàn)橄到y(tǒng)在注冊監(jiān)聽的時(shí)候動(dòng)態(tài)創(chuàng)建了名為NSKVONotifying_A的中間類,并指向這個(gè)中間類了。(isa 指針的作用:每個(gè)對象都有isa 指針,指向該對象的類,它告訴 Runtime 系統(tǒng)這個(gè)對象的類是什么。所以對象注冊為觀察者時(shí),isa指針指向新子類,那么這個(gè)被觀察的對象就神奇地變成新子類的對象(或?qū)嵗┝?。?因而在該對象上對 setter 的調(diào)用就會(huì)調(diào)用已重寫的 setter,從而激活鍵值通知機(jī)制。
②子類setter方法剖析:KVO的鍵值觀察通知依賴于 NSObject 的兩個(gè)方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數(shù)值的前后分別調(diào)用2個(gè)方法:被觀察屬性發(fā)生改變之前,willChangeValueForKey:被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值即將變更;當(dāng)改變發(fā)生后, didChangeValueForKey: 被調(diào)用,通知系統(tǒng)該 keyPath 的屬性值已經(jīng)變更;之后observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用。且重寫觀察屬性的setter 方法這種繼承方式的注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。
上述例子中,當(dāng) p1.name 的值改變時(shí),p1對象的 isa 指針會(huì)指向 NSKVONotifying_Person,意味著,在程序運(yùn)行時(shí),會(huì)動(dòng)態(tài)生成一個(gè) NSKVONotifying_Person 類,該類繼承于 Person,而且該類中也有個(gè) -setName: 方法,方法中在設(shè)置 name 的同時(shí)實(shí)現(xiàn)了:
四、特點(diǎn)
觀察者觀察的是屬性,只有遵循 KVO 變更屬性值的方式才會(huì)執(zhí)行KVO的回調(diào)方法,例如是否執(zhí)行了setter方法、或者是否使用了KVC賦值。
如果賦值沒有通過setter方法或者KVC,而是直接修改屬性對應(yīng)的成員變量,例如:僅調(diào)用_name = @"newName",這時(shí)是不會(huì)觸發(fā)kvo機(jī)制,更加不會(huì)調(diào)用回調(diào)方法的。
所以使用KVO機(jī)制的前提是遵循 KVO 的屬性設(shè)置方式來變更屬性值。
拓展
1.KVC與KVO的不同?
KVC(鍵值編碼),即Key-Value Coding,一個(gè)非正式的Protocol,使用字符串(鍵)訪問一個(gè)對象實(shí)例變量的機(jī)制。而不是通過調(diào)用Setter、Getter方法等顯式的存取方式去問。
KVO(鍵值監(jiān)聽),即Key-Value Observing,它提供一種機(jī)制,當(dāng)指定的對象的屬性被修改后,對象就會(huì)接受到通知,前提是執(zhí)行了setter方法、或者使用了KVC賦值。
2.和notification(通知)的區(qū)別?
notification比KVO多了發(fā)送通知的一步。兩者都是一對多,但是對象之間直接的交互,notification明顯得多,需要notificationCenter來做為中間交互。而KVO如我們介紹的,設(shè)置觀察者->處理屬性變化,至于中間通知這一環(huán),則隱秘多了,只留一句“交由系統(tǒng)通知”,具體的可參照以上實(shí)現(xiàn)過程的剖析。
notification的優(yōu)點(diǎn)是監(jiān)聽不局限于屬性的變化,還可以對多種多樣的狀態(tài)變化進(jìn)行監(jiān)聽,監(jiān)聽范圍廣,例如鍵盤、前后臺(tái)等系統(tǒng)通知的使用也更顯靈活方便。(參照通知機(jī)制第五節(jié)系統(tǒng)通知名稱內(nèi)容)
3.與delegate的不同?
和delegate一樣,KVO和NSNotification的作用都是類與類之間的通信。但是與delegate不同的是:
這兩個(gè)都是負(fù)責(zé)發(fā)送接收通知,剩下的事情由系統(tǒng)處理,所以不用返回值;而delegate 則需要通信的對象通過變量(代理)聯(lián)系;
delegate一般是一對一,而這兩個(gè)可以一對多。
4.涉及技術(shù):
KVC/KVO實(shí)現(xiàn)的根本是Objective-C的動(dòng)態(tài)性和runtime,以及訪問器方法的實(shí)現(xiàn);