iOS 底层探索篇 —— KVO 底层原理(下)
1. 自定义KVO
创建一个NSObject分类,并添加三个方法,lg_addObserver,lg_observeValueForKeyPath和lg_removeObserver。
1.1 lg_addObserver
接下来就是方法的实现。那么对于lg_addObserver,实现的步骤大致如下:
- 验证是否存在setter方法 : 不让成员变量进来
- 动态生成子类
- isa的指向 : LGKVONotifying_LGPerson
- 保存观察者信息,setter方法会使用到这些信息
实现代码如下:
总体流程:
- 验证是否存在setter方法 : 不让成员变量进来
- 动态生成子类
动态生成子类分以下几个步骤
- 防止重复创建生成新类
- 申请类
- 添加class方法
- 添加setter
- 注册类
实现如下:
这里需要展示父类也不是动态子类,所以重写class方法,返回动态子类的父类。
这里的是获取setter方法的名称的实现,也就是将getter的第一个字母变为大写,然后在前面插入set。
setter则是需要调用父类的setter设置新值,然后取出观察者,根据设置的option来对change进行处理,然后发送通知,也就是调用lg_observeValueForKeyPath方法。
注意这里的observer要是weak属性,否则会有循环引用的问题。
1.2 lg_removeObserver
lg_removeObserver实现的步骤大致如下:
- 将储存的关于observer的信息移除
- 将isa重新指回原来的类
实现代码如下:
2. 自定义函数式KVO
lg_addObserver 和 lg_observeValueForKeyPath的话,那么就将逻辑代码和业务代码分开了,能不能用函数式编程思想,将他们放在一起呢?
实现一下。
先为KVO分类添加一个block,并且在addObserver的函数里面添加上这个block。
在保存信息的时候,将block也保存起来。
然后将原来的调用observeValueForket的地方改为调用block。
然后,外界的调用就变成了这样。
3. KVO自动销毁机制
KVO每次都需要手动去调用lg_removeObserver释放,那么能不能做到自动释放呢?
实现一下。
尝试在LGKVO分类里面重写dealloc方法,但是如果这样做的话,就会把所有的东西进行覆盖。
之前学过方法交换,那么是否可以在load的时候,把dealloc拦截下来,然后来到自己的实现里面。但是这样的话,那么所有的dealloc方法都会进行交换,明显不合理。
那么如果在进入KVO的时候进行交换,那么就会有交换多次的问题,还会对原始的dealloc方法有影响。
这里为实现KVO的动态子类里面添加dealloc方法,这样就相当于重写了,然后在实现里面将isa重新指回就可以了。
但是这样做还是不合理的,如果父类的实现里有太多的逻辑的话,那么应该怎么办呢?
4. FBKVOController
FBKVOController是Facebook开源的一个基于系统KVO实现的框架,其使用了函数式编程,可以一行代码实现系统KVO的三个步骤,可以同时对一个对象的多个属性进行监听,线程安全并且可以自动移除观察者。FBKVOController使用了中介者模式,通过创建一个FBKVOController的实例,并将所有监听、回调和释放的工作交过这个实例去完成。
那么FBKVOController是怎么实现自动remove的呢?
在VC中,FBKVOController的实例是VC的一个属性,当VC释放的时候,那么实例作为属性也会进行dealloc。
所以FBKVOController在dealloc里面调用了unobserveAll来进行观察者的释放。
实际调用_unobserveAll。
用_FBKVOSharedController单例调用unobserve来移除所有的观察者。
移除info后再调用removeObserver移除观察者。