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

2018-11-12  本文已影响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实现

//创建一个子类
    NSString *oldName = NSStringFromClass([self class]);
    NSString *newName = [NSString stringWithFormat:@"KVO_%@",oldName];
    //添加类
    Class myClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
    //注册类
    objc_registerClassPair(myClass);
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的解释参考文档

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添加属性。

代码地址:代码地址

上一篇下一篇

猜你喜欢

热点阅读