介绍KVO和KVC
一、什么是kvc
kvc 是 Key-Value-Coding 的简称。
kvc 是一种可以直接通过字符串的名字 key 来访问类属性的机制,而不是通过调用 setter、getter 方法去访问。
我们可以通过在运行时动态的访问和修改对象的属性。而不是在编译时确定,kvc 是 iOS 开发中的黑魔法之一。
1.1 kvc基本语法
获取值
修改值
1.2 kvc的工作原理
直接找setter方法
找property Ivar,进行object_setIvar
找 _property Ivar,进行Ivar赋值
setValue: forUndefinedKey
分析1: 假如我们调用 [[NSObject alloc] setValue:nil forKey:@"property"],其 kvc 调用如下所示:
去模型中查找有没有对应的 setter 方法:例如:setProperty 方法,有就直接调用这个 setter 方法给属性赋值;
如果找不到 setter 方法,接着就会去寻找有没有 property Ivar,如果有,就直接进行 void object_setIvar ( id obj, Ivar ivar, id value ) 赋值;
如果找不到 property 属性,接着又会去寻找 _property Ivar,如果有,直接进行 Ivar 赋值
如果都找不到会调用 setValue: forUndefinedKey:, 然后报出如下所示的崩溃信息。
1.3 kvc例子
用一般的 setter 和 getter,在类外部是不能访问到私有变量的,不能设值给只读变量 用kvc突破限制
修改 TextField 的 placeholder的私有属性
修改 UIPageControl 的图片私有属性
1.4 key和keypath的区别
key :只能接受当前类所具有的属性,不管是自己的,还是从父类继承过来的,如view.setValue(CGRectZero(),key: "frame");
keypath: 除了能接受当前类的属性,还能接受当前类属性的属性,即可以接受关系链,如view.setValue(5,keypath: "layer.cornerRadius")
1.5 kvc访问属性和用点语法访问属性的区别
用点语法编译器会做预编译检查,访问不存在的属性编译器会报错,但是用 kvc 方式编译器无法做检查,如果有错误只能运行的时候才能发现(crash)。
相比点语法用 kvc 方式 kvc 的效率会稍低一点,但是灵活{coderiding解析:灵活的意思是当你在编译前不确定你要访问什么属性,而要等到运行的时候,根据具体情况而言来访问,这时候kvc就有它的价值},可以在程序运行时决定访问哪些属性。
用 kvc 可以访问对象的私有成员变量。
二、kvo的本质
kvo 的本质就是监听对象的属性进行赋值的时候有没有调用 setter 方法
kvo 是 Key-Value-Observing 的简称。
kvo 是一个观察者模式。观察一个对象的属性,注册一个指定的路径,若这个对象的的属性被修改,则 kvo 会自动通知观察者。
更通俗的话来说就是任何对象都允许观察其他对象的属性,并且可以接收其他对象状态变化的通知。
每次对象的属性被改变后,那么监听者就会收到通知
kvo 是通知的一种,还有的通知就是 NSNotificationCenter
在ObjC中要实现kvo则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用kvo。
一个对象,可以被多个观察,所以每次销毁的时候就要移除观察
kvo 是一个对象能观察另一个对象属性的值,kvo
适合任何对象监听另一个对象的改变,这是一个对象与另外一个对象保持同步的一种方法。kvo 只能对属性做出反应,不会用来对方法或者动作做出反应。
优点:
提供一个简单的方法来实现两个对象的同步。
能够提供观察的属性的新值和旧值。
每一次属性值改变都是自动发送通知,不需要开发者手动实现。
用 keypath 来观察属性,因此也可以观察嵌套对象。
缺点:
观察的属性必须使用字符串来定义,因此编译器不会出现警告和检查
只能重写回调方法来后去通知,不能自定义 selector。当观察多个对象的属性时就要写"if"语句,来判断当前的回调属于哪个对象的属性的回调。
2.1 kvo原理:
当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。 派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。 同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
2.2 kvo语法
2.3 kvo观察例子
监听 ScrollView 的 contentOffSet 属性
kvo选择不监听某个属性
kvo通过po查看被观察对象的所有观察信息
2.4 kvo和线程
一个需要注意的地方是,kvo 行为是同步的,并且发生与所观察的值发生变化的同样的线程上。没有队列或者 Run-loop 的处理。手动或者自动调用 -didChange... 会触发 kvo 通知。
所以,当我们试图从其他线程改变属性值的时候我们应当十分小心,除非能确定所有的观察者都用线程安全的方法处理 kvo 通知。通常来说,我们不推荐把 kvo 和多线程混起来。如果我们要用多个队列和线程,我们不应该在它们互相之间用 kvo。
kvo 是同步运行的这个特性非常强大,只要我们在单一线程上面运行(比如主队列 main queue),kvo 会保证下列两种情况的发生:
首先,如果我们调用一个支持 kvo 的 setter 方法,如下所示:
kvo 能保证所有 exchangeRate 的观察者在 setter 方法返回前被通知到。
其次,如果某个键被观察的时候附上了 NSKeyValueObservingOptionPrior 选项,直到 -observe... 被调用之前, exchangeRate 的 accessor 方法都会返回同样的值。
2.5 kvo使用注意
首先,KVO 兼容是 API 的一部分。如果类的所有者不保证某个属性兼容 KVO,我们就不能保证 KVO 正常工作。苹果文档里有 KVO 兼容属性的文档。例如,NSProgress 类的大多数属性都是兼容 KVO 的。
当做出改变以后,有些人试着放空的 -willChange 和 -didChange 方法来强制 KVO 的触发。KVO 通知虽然会生效,但是这样做破坏了有依赖于 NSKeyValueObservingOld 选项的观察者。详细来说,这影响了 KVO 对观察键路径 (key path) 的原生支持。KVO 在观察键路径 (key path) 时依赖于 NSKeyValueObservingOld 属性。
我们也要指出有些集合是不能被观察的。KVO 旨在观察关系 (relationship) 而不是集合。我们不能观察 NSArray,我们只能观察一个对象的属性——而这个属性有可能是 NSArray。举例说,如果我们有一个 ContactList 对象,我们可以观察它的 contacts 属性。但是我们不能向要观察对象的 -addObserver:forKeyPath:... 传入一个 NSArray。
相似地,观察 self 不是永远都生效的。而且这不是一个好的设计。
2.6 kvo底层原理剖析
剖析:
self.person = [[Person alloc] init];
系统会动态创建一个继承于 Person 的 NSKVONotifying_Person
person 的 isa 指针指向的类 Person 变成 NSKVONotifying_Person,所以接下来的 person.age = newAge 的时候,他调用的不是 Person 的 setter 方法,而是 NSKVONotifying_Person(子类)的 setter 方法
重写NSKVONotifying_Person的setter方法:[super setName:newName]
通知观察者告诉属性改变。
派生类 NSKVONotifying_Person 剖析:
在这个过程,被观察对象的 isa 指针从指向原来的 Person 类,被 KVO 机制修改为指向系统新创建的子类 NSKVONotifying_Person 类,来实现当前类属性值改变的监听。
所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对 KVO 的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为 NSKVONotifying_Person 的类(),就会发现系统运行到注册 KVO 的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为 NSKVONotifying_Person 的中间类,并指向这个中间类了。
因而在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。这也是 KVO 回调机制,为什么都俗称 KVO 技术为黑魔法的原因之一吧:内部神秘、外观简洁。
三、kvc和kvo的keyPath一定是属性么?
kvc 支持实例变量
kvo 只能手动支持手动设定实例变量的kvo实现监听
Last updated
Was this helpful?