美文网首页
KVO底层实现,使用runtime自定义KVO

KVO底层实现,使用runtime自定义KVO

作者: 追寻那一米阳光 | 来源:发表于2018-11-12 10:44 被阅读57次
    KVO底层原理

    1.当一个object有观察者时,动态创建这个object的类的子类,子类命名为NSKVONotifying_ClassName
    2.对于每个被观察的property在子类NSKVONotifying_ClassName中重写其seter方法
    3.在重写的set方法中调用- willChangeValueForKey:- didChangeValueForKey:通知观察者
    4.当一个property没有观察者时,删除重写的方法
    5.当没有observer观察任何一个property时,删除动态创建的子类
    注意:
    使用KVO时会生成一个被观察类的子类,但是这个类是被隐藏掉的,使用普通的class方法无法获取.因为子类里边重写了- class方法,返回正常的类.但是在控制台里边使用po object_getClass(id (对象名称)) 打印,打印结果为:NSKVONotifying_ClassName

    自定义KVO实现

    • 创建NSObject的分类
    • 创建一个子类、注册
    //创建一个子类
        NSString *oldName = NSStringFromClass([self class]);
        NSString *newName = [NSString stringWithFormat:@"KVO_%@",oldName];
        //添加类
        Class myClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
        //注册类
        objc_registerClassPair(myClass);
    
    • 重写set方法,给myClass添加setName方法
    NSString *selector_name = [NSString stringWithFormat:@"set%@:",[keyPath capitalizedString]];
            SEL method = NSSelectorFromString(selector_name);
        class_addMethod(myClass, method, (IMP)setName, "v@:@");
    

    OC中每个方法都有两个默认参数 id self方法调用者 与SEL _cmd调用方法的标号
    class_addMethod"v@:@"Type Encodings的解释参考文档

    • 重写seter方法
    void setName(id self,SEL _cmd,NSString *newName){
        struct objc_super superClass = {self,class_getSuperclass([self class])};
        //修改name属性
        objc_msgSendSuper(&superClass,_cmd,newName);
        //取出全局属性block
        void(^block)(id,NSDictionary *) = objc_getAssociatedObject(self, @"block");
        if (block) {
            block([self superclass],@{@"key":newName});
        }
    //[self superclass]为了满足隐藏子类的操作
    }
    

    思路就是通过runtime动态创建子类,给子类重写父类的seter方法,在重写的seter方法中添加监听机制,我这里使用的是block回调,因为分类不能添加属性,所以要使用全局属性的时候只能通过runtime添加属性。

    代码地址:代码地址

    相关文章

      网友评论

          本文标题:KVO底层实现,使用runtime自定义KVO

          本文链接:https://www.haomeiwen.com/subject/vgyqfqtx.html