KVO的使用
要實現will/didChangeValueForKey:方法
kvo的例項 實際在執行時被呼叫
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;複製程式碼
觸發
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;複製程式碼
因此要實現KVO,需要下列條件之一即可
- 實現了KVC
- 有訪問器方法(KVO後,會執行時重寫setter方法,新增willChangeValueForKey,didChangeValueForKey)
- 顯示呼叫will/didChangeValueForKey:
KVO實現原理
依賴於Runtime
- isa的改變
- 動態建立子類
- 替換重寫setter方法
- 重寫class方法
動態建立被觀察物件的子類,重寫setter方法,並且要管理一個物件的所有觀察者.
- 動態建立子類
// 子類名
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方法。管理觀察者
由於一個物件可能被多個觀察者觀察,所以可以用關聯物件的方法來管理所有的觀察者。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方法
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…