iOS 笔记iOS面试题+基础知识

KVO底层实现分析

2018-08-23  本文已影响58人  Jacob_LJ

注:分析参考 MJ底层原理班 内容,本着自己学习原则记录

1 KVO 的基本使用

1.1 给 Person 的实例对象 person 添加observer,监听 person 的 age 属性

self.person = [[Person alloc] init];
...
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person addObserver:self forKeyPath:@"age" options:options context:@"123"];

1.2 当 person 的 age 属性发生变化时,会调用 observer 的回调方法(也就是NSObject(NSKeyValueObserving)分类的方法)

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

2 NSKVONotifying_Person

2.1 执行 KVO 的person1对象,它的 isa 所指向的类对象不再是 Person 类对象,而是 NSKVONotifying_Person 类对象

证明方式:

  1. 断点打印


  2. 通过 runtime 的 object_getClass() 方法获取实例对象对应的类对象,证明person 实例对象的类对象确实被系统改写了


3 NSKVONotifying_Person 中 setAge: 的内部执行逻辑猜想

3.1 证明setAge:方法内部调用的是 _NSSetIntValueAndNotify() 方法

  1. 通过打印 setAge: 方法的地址判断前后变化


  1. 通过LLDB 命令,将方法地址转成名称形式显示


注意:
_NSSetIntValueAndNotify()是会根据监听属性的类型进行变化的,前面 age 属性的描述为@property (assign, nonatomic) int age;。当 age 属性描述变更为 double 的话,即@property (assign, nonatomic) double age;,setAge:方法内调用的方法为_NSSetDoubleValueAndNotify()

3.2 证明 NSKVONotifying_Person 的isa 指向什么?(是 Person 的元类对像吗?)

注意object_getClass(object_getClass(self.person1)) 表示,获取 self.person1的元类对象,即第一次object_getClass(self.person1)是获取 person1的类对象,第二次object_getClass则是获取类对象的元类对象。

3.3 证明 NSKVONotifying_Person 的 superClass 指向什么?(是 Person 类吗?)


关系如下:


3.4 证明 _NSSetIntValueAndNotify()方法内部实现确实如猜想代码中的调用逻辑

  1. 通过在 NSKVONotifying_Person 的父类 Person 中(3.2中证明了),重写willChangeValueForKey:didChangeValueForKey:方法,拦截调用顺序,具体代码如图:
  2. 点击屏幕,改变 person1的 age 属性值,对比 log 日志打印信息


为什么要通过在 Person 中重写上述方法拦截打印来证明呢?

  1. NSKVONotifying_Person的父类为 Person 类,当在 Person 类类中重写了上述方法,那么子类可以通过 superclass 指针优先找到Person类中方法实现
  2. willChangeValueForKey:didChangeValueForKey:方法是属于NSObject 的NSObject(NSKeyValueObserverNotification)分类里的方法,此时的NSObject 是 Person 的父类,按照实例方法的查找调用循序来看,当然会优先调用 Person 中重写的方法,而 Person 中的方法仅仅是重写拦截,内部还会通过 super 调用父类的方法,所以对原有的 NSObject 的分类方法是没有影响的。
  1. 通过log 日志可知,_NSSet***ValueAndNotify 的内部实现为:

4 相关题:

4.1 iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

  1. 利用Runtime API动态生成一个子类,并且让instance对象的isa指向这个全新的子类
  2. 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
  3. _NSSet***ValueAndNotify 的内部实现为:

4.2 如何手动触发KVO?

4.3 直接修改成员变量会触发KVO么?


文/Jacob_LJ(简书作者)
PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载需联系作者获得授权,并注明出处,所有打赏均归本人所有!

上一篇 下一篇

猜你喜欢

热点阅读