欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS窥探KVO底层实现实战篇

程序员文章站 2024-03-24 13:42:28
...

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!

iOS窥探KVO底层实现实战篇iOS窥探KVO底层实现实战篇


作者:大兵布莱恩特

链接:https://www.jianshu.com/p/dc89f0a2d1ac

iOS开发整理发布,转载请联系作者获得授权


文讲到 iOS KVO 底层实现原理https://www.jianshu.com/p/0aa83ac521ba,大概就是runtime时候动态的创建一个子类,并重写了子类的 setter dealloc class 等方法,将当前类的 isa 指针指向这个子类,这样就不会影响原有类的实现

iOS窥探KVO底层实现实战篇

上图可以看到 KVO内部执行顺序

今天我们就 kvo 内部执行顺序 也通过 runtime 动态创建子类方式去实现.

第一步动态创建一个 NSKVONotifying_Person 子类

/**
 运行时动态的创建子类

 @param super_cls 父类
 @return 返回子类
 */
- (Class) registerSubClassWithSuperClass:(Class)super_cls  {
    ///动态的创建 子类
    NSString *clsName = [NSString stringWithFormat:@"NSKVONotifying_%@",super_cls];
    ///一个 NSObject 默认分配16个字节内存
    Class sub_cls = objc_allocateClassPair(super_cls,clsName.UTF8String,16);
    ///注册一个子类
    objc_registerClassPair(sub_cls);
    ///将父类 isa 指针指向 子类
    object_setClass(self, sub_cls);
    return sub_cls;
}

第二步动态的给这个子类 动态添加方法 setter 方法 didChangeValueForKey方法 class 方法实现


    ///动态创建子类  NSKVONotifying_xxx
    Class sub_cls = [self registerSubClassWithSuperClass:super_cls];

    ///给子类动态的添加 class setter  didChangeValueForKey 实现
    Method class_method = class_getInstanceMethod(super_cls, @selector(class));
    Method changeValue_method = class_getInstanceMethod(super_cls, @selector(didChangeValueForKey:));

    class_addMethod(sub_cls, @selector(class), (IMP)kvo_class,method_getTypeEncoding(class_method));
    ///给子类动态的添加 didChangeValueForKey
    class_addMethod(sub_cls, @selector(didChangeValueForKey:), (IMP)didChangeValue,method_getTypeEncoding(changeValue_method));
    ///动态的给子类添加 setter 方法
    class_addMethod(sub_cls, setterSel, (IMP)kvo_setter,method_getTypeEncoding(method));

    ///将观察者对象跟当前实例 self 关联起来
    objc_setAssociatedObject(self,(__bridge const void * _Nonnull)(KVOAssociatedObservers), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

重写 class 方法实现

/**
 自实现 class 方法

 @param self 当前类实现
 @param _cmd  class
 @return  返回父类 Class 外界不会知道 NSKVONotifying_子类存在
 */
static Class kvo_class(id self,SEL _cmd) {
    return class_getSuperclass(object_getClass(self));
}

重写 setter 方法实现

/**
 自实现 setter 方法

 @param self 当前类实现
 @param _cmd  setter
 @param newValue  赋值
 */
static void kvo_setter(id self,SEL _cmd,id newValue) {

    NSString *setterName = NSStringFromSelector(_cmd);
    NSString *getterName = getterForSetter(setterName);

    ///将要改变属性的值
    [self willChangeValueForKey:getterName];

    ///调用 super setter 方法
    struct objc_super suer_cls = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };

    ///存储旧值
    objc_setAssociatedObject(self,(__bridge const void * _Nonnull)(KVOAssociatedOldValue),[self valueForKey:getterName], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    ///调用父类 setter 方法 设置新值
    objc_msgSendSuper(&suer_cls,_cmd,newValue);
    ///改变监听属性值后 调用 didChangeValueForKey 并在内部 调用
    [self didChangeValueForKey:getterName];

};

重写 didChangeValueForKey 方法实现

/**
 didChangeValueForkey 实现方法 , 当根据 SEL (didChangeValueForkey:) 会找到方法 IMP 实现
 */
static void didChangeValue(id self,SEL _cmd,NSString *key) {

    id newValue = [self valueForKey:key];
    id observer = objc_getAssociatedObject(self,(__bridge const void * _Nonnull)(KVOAssociatedObservers));
    id oldValue = objc_getAssociatedObject(self,(__bridge const void * _Nonnull)(KVOAssociatedOldValue));

    NSMutableDictionary *change = [NSMutableDictionary dictionary];
    if (oldValue) {
        change[@"oldValue"] = oldValue;
    } else {
        change[@"oldValue"] = [NSNull null];
    }
    if (newValue) {
        change[@"newValue"] = newValue;
    } else {
        change[@"newValue"] = newValue;
    }

    [observer observeValueForKeyPath:key ofObject:self change:change context:NULL];

}

通过以上的步骤 就可以模拟 KVO 内部实现 ,写出来一个自己的 KVO 实现

iOS窥探KVO底层实现实战篇

从代码运行来看 p1添加了 kvo 监听 ,当p1.name 发生两次改变时候 都有调用 observeValueForKeyPath方法

p2没有添加 kvo 监听 所以只是简单的调用了 person 的 setter 方法

以上代码只是对 KVO 做了一个比较简单的实现 ,并没有做一些释放操作 将子类 isa 指针重新执行 Person 类 ,本文只探究原理 实际开发中可能并不会自己去实现 KVO, 只需要调用系统 API 即可

iOS窥探KVO底层实现实战篇iOS窥探KVO底层实现实战篇【点击成为源码大神】

▼点击「阅读原文」进入程序员商城