這篇主要講KVO的底層原理,順便講一下KVC。
先說一下KVC吧,KVC(key-value-coding)提供了一種通過字符串訪問對象屬性的機制,而不是調(diào)用對象的setter、getter方法。NSObject一個分類中實現(xiàn)了KVC,所以O(shè)C中所有對象都可以使用KVC。
對于對象的成員變量使用點語法(setter和getter方法)和KVC差別不大,但KVC神奇的是可以訪問私有屬性,也就是沒有提供setter和getter方法的屬性。舉個例子:
UIPageControl*pageControl = [[UIPageControlalloc] init];
?[pageControl setValue:[UIImageimageNamed:@"xxx.png"] forKeyPath:@"_pageImage"];
?[pageControl setValue:[UIImageimageNamed:@"xxx.png"] forKeyPath:@"_currentPageImage"]
_pageImage和_currentPageImage并不是UIPageControl對外公布的屬性,直接用點語法是訪問不到的,所以這里就使用了KVC進行賦值。
接著來講今天的重點:KVO(Key-Value-Observe)
KVO本質(zhì)上是觀察者模式的一種實現(xiàn),它提供一種機制,當(dāng)指定對象的屬性被修改后,則對象就會接受到通知。簡單的說,就是每次指定的被觀察的對象的屬性被修改后,KVO就會自動通知相應(yīng)的觀察者了。具體用法:
?Person?*p?=?[[Person?alloc]?init];??
?p.age?=?20; ? //原始值
?[p?addObserver:self?forKeyPath:@"age"?options:NSKeyValueObservingOptionOld?|?NSKeyValueObservingOptionNew?context:nil];??//注冊觀察者 ?
p.age=30;//修改值,此時便會觸發(fā)KVO提供的監(jiān)聽函數(shù):
-?(void)observeValueForKeyPath:(NSString?*)keyPath?ofObject:(id)object?change:(NSDictionary?*)change?context:(void?*)context?{??
????NSLog(@"%@對象的%@屬性改變了,由舊值%@改變?yōu)樾轮?@",?object,?keyPath,?change[@"old"],?change[@"new"]);??
}??
那么KVO具體是怎么實現(xiàn)的呢?
一句話總結(jié):對象在第一次添加監(jiān)聽的時候,系統(tǒng)會動態(tài)的新生成一個原對象類的子類,結(jié)合上面的例子就是NSKVONotifying_Person類,然后重寫該子類的setter方法,重寫的setter方法里面回調(diào)用willChangeValue和didChangeValue方法。同時,原對象的isa指針會指向新生成的子類。這樣,當(dāng)對象再調(diào)用屬性的setter方法時,其實調(diào)用的是子類的settet方法,從而實現(xiàn)屬性變化的監(jiān)聽。
這里是通過runtime動態(tài)生成類,涉及到方法有:
object_getClass(p)//獲得對象所屬的類,也就是isa指針
object_setClass(p, newClass);//設(shè)置對象所屬的類,改變isa指針的指向
class_addMethod(newClass,selectorName,?(IMP)newsetter,types)//類添加方法及實現(xiàn)
Class newClass=objc_allocateClassPair(originClass,?kvoClassName.UTF8String,?0)//新申請指定名字的類
objc_registerClassPair(newClass)//向系統(tǒng)注冊新申請的類
總結(jié):其實是新的類,新的方法去做的監(jiān)聽,雖然看上去像是原來的類。