KVO實現原理和代碼實現

鍵值觀察通知依賴於NSObject兩個方法:willChangeValueForKey:和didChangeValueForKey:在一個被觀察屬性發生改變之前,willChangeValueForKey:一定會被調用,這就會記錄舊的值。而改變發生後:observerValueForKey:ofObject:change:context:會被調用,繼而didChangeValuueForKey:也會被調用。
在使用KVC命名約定時,當觀察一個對象時,一個新的類會被動態創建。這個類繼承自該對象的原本的類,並重寫了被觀察屬性的setter方法。重寫的setter方法會負責在調用setter方法之前和之後,通知所有觀察對象值的更改。最後通過isa混寫把這個對象的isa指針指向這個新創建的子類。
在這裏插入圖片描述

- (void)Jay_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(JayObservingBlock)block{
//獲取set方法的選擇器
    SEL setterSelector = NSSelectorFromString(setterForGetter(key));
    Method setterMetod = class_getInstanceMethod([self class], setterSelector);
    if (!setterMetod) {
        NSString *reason = [NSString stringWithFormat:@"Object %@ does not have a setter for key %@",self,key];
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
        return;
    }
    Class clazz = object_getClass(self);
    NSString *clazzName = NSStringFromClass(clazz);
//創建該對象所屬類的觀察者子類,並將isa指針指向觀察者類
    if (![clazzName hasPrefix:kJayKVOClassPrefix]) {
        clazz = [self makeKVOClassWithOriginalClassName:clazzName];
        object_setClass(self, clazz);
    }
//往所創建的觀察者子類裏添加setter方法實現
    if (![self hasSelector:setterSelector]) {
        const char *types = method_getTypeEncoding(setterMetod);
        class_addMethod(clazz, setterSelector, (IMP)kvo_setter, types);
    }
    //同時保存觀察者的信息,觀察者註冊完成
    JayObservationInfo *infoObj = [[JayObservationInfo alloc]initWithObserver:observer key:key block:block];
    NSMutableArray *observersArr = objc_getAssociatedObject(self, (__bridge const void *)(kJayKVOAssociatedObservers));
    if (!observersArr) {
        observersArr = [NSMutableArray array];
        objc_setAssociatedObject(self, (__bridge const void *)(kJayKVOAssociatedObservers), observersArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    [observersArr addObject:infoObj];
    
}

https://github.com/Sounniy/KVODemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章