KVO的实现原理: 核心是重写子类的set方法进行监听
具体是:当某一个类在注册监听的时候,系统会动态(1)创建一个继承此类的一个子类(2),并用isa指针(3)指向该子类,当我们改变某一个属性值时,KVO会重写子类的 setter 方法(4),setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况
(1)C语言创建某一个类的子类:
//动态添加一个类
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [@"NSKVONotifying_" stringByAppendingString:oldClassName];
const char * newName = [newClassName UTF8String];
Class myclass = objc_allocateClassPair([self class],
newName, 0);
注:objc_allocateClassPair(Class superclass,constchar*name, size_t extraBytes) 添加类 superclass 类是父类 name 类的名字 size_t 类占的空间
(2)当观察对象A时,KVO机制动态创建一个新的名为:NSKVONotifying_A的新类,该类继承自对象A的本类.
NSKVONotifying_A 类剖析:在这个过程,被观察对象的 isa 指针从指向原来的 A 类,被 KVO 机制修改为指向系统新创建的子类 NSKVONotifying_A 类,来实现当前类属性值改变的监听;
(3)isa指针的作用:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。
(4)子类setter方法剖析:KVO 的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用 2 个方法:
被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的 setter 方法这种继承方式的注入是在运行时而不是编译时实现的
注意:观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行 KVO 的回调方法,例如是否执行了 setter 方法、或者是否使用了 KVC 赋值。
如果赋值没有通过 setter 方法或者 KVC,而是直接修改属性对应的成员变量,例如:仅调用 _name = @"newName",这时是不会触发 KVO 机制,更加不会调用回调方法的。
所以使用 KVO 机制的前提是遵循 KVO 的属性设置方式来变更属性值。
附加:
苹果为什么要用子类(就是C语言创建的那个子类)监听setter方法,而不用category类(Person+KVO)呢?
原因是当你用分类监听setter方法的时候,Person类中setter方法就不会走了,这样不好,所以苹果使用了子类监听setter方法。
KVC 与 KVO 的不同?
KVC(键值编码),即 Key-Value Coding,一个非正式的 Protocol,使用字符串(键)访问一个对象实例变量的机制。而不是通过调用 Setter、Getter 方法等显式的存取方式去访问。
KVO(键值监听),即 Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,对象就会接受到通知,前提是执行了 setter 方法
KVO和 notification(通知)的区别?
notification 比 KVO 多了发送通知的一步。
两者都是一对多,但是对象之间直接的交互,notification 明显得多,需要notificationCenter 来做为中间交互。而 KVO 如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,则隐秘多了,只留一句“交由系统通知”
notification 的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,例如键盘、前后台等系统通知的使用也更显灵活方便。
KVO 和线程
KVO 行为是同步的,并且发生与所观察的值发生变化的同样的线程上。没有队列或者 Run-loop 的处理.所以不是线程安全的,当我们试图从其他线程改变属性值的时候我们应当十分小心,除非能确定所有的观察者都用线程安全的方法处理 KVO 通知。通常来说,我们不推荐把 KVO 和多线程混起来。如果我们要用多个队列和线程,我们不应该在它们互相之间用 KVO
详细自定义kvo传送门
[导致kvo崩溃的点](https://www.jianshu.com/p/a3acab9dfc4a)