iOS 深刻理解KVO實現 | 掘金技術徵文

KVO的使用

要實現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

  1. 實現了KVC
  2. 有訪問器方法(KVO後,會運行時重寫setter方法,添加willChangeValueForKey,didChangeValueForKey)
  3. 顯示調用will/didChangeValueForKey:

KVO實現原理

依賴於Runtimeui

  1. isa的改變
  2. 動態建立子類
  3. 替換重寫setter方法
  4. 重寫class方法

動態建立被觀察對象的子類,重寫setter方法,而且要管理一個對象的全部觀察者.spa

  1. 動態建立子類
// 子類名
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;複製代碼
  1. 改變isa指針
    isa指針的做用: isa指針指向實例的類(對於這裏的狀況)
    實例經過isa指針找到類,能夠獲得方法列表,屬性列表等信息
    當咱們將isa指針指向子類時,就能夠調用子類的方法,使用子類的屬性等。
    因而,調用該實例的setter方法實際上是調用了子類的setter方法指針

  2. 管理觀察者
    因爲一個對象可能被多個觀察者觀察,因此能夠用關聯對象的方法來管理全部的觀察者。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];複製代碼
  3. 重寫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.classobject_getClass(object)進行判斷 來防止KVO致使的AOP無效複製代碼

「掘金技術徵文」
juejin.im/post/58d8e9…

相關文章
相關標籤/搜索