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

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