kvo实现原理_iOS窥探KVO底层实现实战篇

原标题:iOS窥探KVO底层实现实战篇

作者丨大兵布莱恩特

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

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

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

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

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

/**

运行时动态的创建子类

@param super_cls 父类

@return 返回子类

*/

- (Class) registerSubClassWithSuperClass:(Class)super_cls {

///动态的创建 子类

NSString*clsName = [ NSStringstringWithFormat: @"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);

returnsub_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 constvoid* _Nonnull)(KVOAssociatedObservers), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

重写 class 方法实现

/**

自实现 class 方法

@paramself 当前类实现

@param_cmd class

@return返回父类 Class 外界不会知道 NSKVONotifying_子类存在

*/

staticClass kvo_class(id self,SEL _cmd){

returnclass_getSuperclass(object_getClass(self));

}

重写 setter 方法实现

/**

自实现 setter 方法

@param self 当前类实现

@param _cmd setter

@param newValue 赋值

*/

staticvoidkvo_setter( idself,SEL _cmd, idnewValue) {

NSString*setterName = NSStringFromSelector(_cmd);

NSString*getterName = getterForSetter(setterName);

///将要改变属性的值

[ selfwillChangeValueForKey:getterName];

///调用 super setter 方法

structobjc_super suer_cls = {

.receiver = self,

.super_class = class_getSuperclass(object_getClass( self))

};

///存储旧值

objc_setAssociatedObject( self,(__bridge constvoid* _Nonnull)(KVOAssociatedOldValue),[ selfvalueForKey:getterName], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

///调用父类 setter 方法 设置新值

objc_msgSendSuper(&suer_cls,_cmd,newValue);

///改变监听属性值后 调用 didChangeValueForKey 并在内部 调用

[ selfdidChangeValueForKey:getterName];

};

重写 didChangeValueForKey 方法实现

/**

didChangeValueForkey 实现方法 , 当根据 SEL (didChangeValueForkey:) 会找到方法 IMP 实现

*/

staticvoiddidChangeValue( idself,SEL _cmd, NSString*key) {

idnewValue = [ selfvalueForKey:key];

idobserver = objc_getAssociatedObject( self,(__bridge constvoid* _Nonnull)(KVOAssociatedObservers));

idoldValue = objc_getAssociatedObject( self,(__bridge constvoid* _Nonnull)(KVOAssociatedOldValue));

NSMutableDictionary*change = [ NSMutableDictionarydictionary];

if(oldValue) {

change[ @"oldValue"] = oldValue;

} else{

change[ @"oldValue"] = [ NSNullnull];

}

if(newValue) {

change[ @"newValue"] = newValue;

} else{

change[ @"newValue"] = newValue;

}

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

}

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

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

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

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

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值