要實現will/didChangeValueForKey:方法javascript
kvo的實例 實際在運行時被調用java
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;複製代碼
觸發數組
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;複製代碼
所以要實現KVO,須要下列條件之一便可post
依賴於Runtimeui
動態建立被觀察對象的子類,重寫setter方法,而且要管理一個對象的全部觀察者.spa
// 子類名
NSString *kvoclassName = [kXJYKVOPrefix stringByAppendingString:originalclassName];
Class class = NSClassFromString(kvoclassName);
if (class) {
return class;
}
// class doesn't exist yet, make it
Class originalclass = object_getClass(self);
Class kvoclass = objc_allocateClassPair(originalclass, kvoclassName.UTF8String, 0);
// 得到簽名
Method classMethod = class_getInstanceMethod(originalclass, @selector(class));
const char *types = method_getTypeEncoding(classMethod);
// 替換setter 實現
class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, types);
objc_registerClassPair(kvoclass);
return kvoclass;複製代碼
改變isa指針
isa指針的做用: isa指針指向實例的類(對於這裏的狀況)
實例經過isa指針找到類,能夠獲得方法列表,屬性列表等信息
當咱們將isa指針指向子類時,就能夠調用子類的方法,使用子類的屬性等。
因而,調用該實例的setter方法實際上是調用了子類的setter方法。指針
管理觀察者
因爲一個對象可能被多個觀察者觀察,因此能夠用關聯對象的方法來管理全部的觀察者。code
XJYObservationInfo *info = [[XJYObservationInfo alloc]initWithObserver:self key:key block:block];
// 維護改KVO觀察者數組
NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kXJYKVOAssociatedObservers));
if (!observers) {
observers = [NSMutableArray array];
objc_setAssociatedObject(self, (__bridge const void *)(kXJYKVOAssociatedObservers), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observers addObject:info];複製代碼
重寫class方法server
class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, types);
static Class kvo_class(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}複製代碼
還記得Aspects中 對於KVO的特殊處理嗎,KVO改變了實例對象的isa指針,在此處 Aspects對KVO過的實例進行了特殊的處理
Aspects:對象
// Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
else if (statedClass != baseClass) {
return aspect_swizzleClassInPlace(baseClass);
}複製代碼
object.class 因爲KVO重寫了class方法,因此不能準確的找到類 object_getClass()方法能夠準確的找到isa指針 object.class 與 object_getClass(object)進行判斷 來防止KVO致使的AOP無效複製代碼
「掘金技術徵文」
juejin.im/post/58d8e9…