KVC和KVO

来源:互联网 发布:淘宝买手机店铺推荐 编辑:程序博客网 时间:2024/06/02 03:04

** KVC:

   * 1.字典转模型 ,简化代码量

 

   * 2.修改系统的只读变量:例如自定义tabBar的时候,由于tabBar是只读属性,只能用KVC赋值.

 

   * 3.可以任意修改一个对象的属性和变量\(包括私有变量\)

 

   * 4.可以通过运算符层次查找对象的属性,setValue:forKeyPath:

假如person这个类中我们又有个属性dog,Dog类中又有个属性名字那么我们怎么通过'p'这个对象去设置狗的属性呢?

 

 //初始化Dog对象

p.dog= [[Dog alloc] init];

//dog对象赋值

[p setValue:@"旺财" forKey:@"dog.name"];

 

结果:

逐级寻找key错误演示.png

 

 

如果我们使用 setValue:forKey:这个方法,Xcode会报错说找不到dog.name这个key,想想我们在stroyboard中,如果我们控件连线出现错误,也会报相似的错误,说明了stroyboard在赋值的时候也是通过kvc的方式来操作的

 

现在我们来试试用 setValue:forKeyPath:方法

 

[p setValue:@"旺财"forKeyPath:@"dog.name"];

 

结果:

   

逐级寻找key演示.png

成功了,说明 setValue:forKeyPath: 方法中包含了 setValue:forKeyPath:的方法,但是内部增加了更高级的功能 —— 内部实现:它会先去 person类中寻找有没有 dog这个key,如果有,那么会去Dog类中寻找有没有name这个key,如果有,就给name这个key赋值

 

KVC的缺点:

一旦使用 KVC 编译器将无法检查出错误,即不会对设置的键、键路径进行错误检查,且执行效率要低于自定的 setter 和getter 方法。因为使用 KVC 键值编码,它必须先解析字符串,然后在设置或者访问对象的实例变量。

 

 

 

* * KVO:

在 Objective-C 中如何实现 KVO

  • 注册观察者(注意:观察者和被观察者不会被保留也不会被释放)
    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
    options:(NSKeyValueObservingOptions)options
    context:(void*)context;

 

  • 接收变更通知
    - (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object change:(NSDictionary *)change   context:(void*)context;

 

  • 移除对象的观察者身份
    - (void)removeObserver:(NSObject *)observer
    forKeyPath:(NSString *)keyPath;
  • KVO中谁要监听谁注册,然后对响应进行处理,使得观察者与被观察者完全解耦。KVO只检测类中的属性,并且属性名都是通过NSString来查找,编译器不会检错和补全,全部取决于自己。

 

 

KVO的底层实现:

KVO是基于runtime机制实现的/small-talk

  1. 当某个类的属性对象第一次被观察时,系统就会在运行期间动态地创建该类的一个子类,在这个子类中重写父类的任何被观察属性的setter方法。子类在被重写的setter方法内实现真正的通知机制
  2. 如果原类为Person,那么生成的子类名为NSKVONotifying_Person
  3. 我们知道,每一个类中都有一个isa指针指向当前类,所有系统就是在当一个类的对象第一次被观察的时候,系统就会偷偷将isa指针指向动态生成的子类,从而在被监听属性赋值时被执行的是子类的setter方法
  4. 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
  5. 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的子类:如果我们创建一个新的名为“NSKVONotifying_A”的类(),就会发现系统运行到注册KVO的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为NSKVONotifying_A的中间类,并指向这个中间类了。

 

isa 指针的作用:每个对象都有isa指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。

 

 

总结:

KVC/KVO实现的根本是Objective-C的动态性和runtime

对比其他的回调方式,KVO机制的运用的实现,更多的由系统支持,相比notification、delegate等更简洁些,并且能够提供观察属性的最新值以及原始值;但是相应的在创建子类、重写方法等等方面的内存消耗是很巨大的。所以对于两个类之间的通信,我们可以根据实际开发的环境采用不同的方法,使得开发的项目更加简洁实用。

 

另外需要注意的是,由于这种继承方式的注入是在运行时而不是编译时实现的,如果给定的实例没有观察者,那么KVO不会有任何开销,因为此时根本就没有KVO代码存在。但是即使没有观察者,委托和NSNotification还是得工作,这也是KVO此处零开销观察的优势。

0 0
原创粉丝点击