码迷,mamicode.com
首页 > 其他好文 > 详细

OC之KVC,KVO

时间:2015-10-25 09:40:31      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:

KVO简介

在 Cocoa 的模型-视图-控制器 (Model-view-controller)架构里,控制器负责让视图和模型同步。这一共有两步:当 model 对象改变的时候,视图应该随之改变以反映模型的变化;当用户和控制器交互的时候,模型也应该做出相应的改变。

KVO 能帮助我们让视图和模型保持同步。控制器可以观察视图依赖的属性变化。

 

1.使用:

1.1.注册与解除注册 

NSKeyValueObserverRegistration 的 category 方法将观察者对象与被观察者对象注册与解除 注册: 

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath opt ions:(NSKeyValueObservingOptions)options context:(void *)context;

 

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

这两个方法的定义在 Foundation/NSKeyValueObserving.h 中 

 


1.2.处理变更通知
观察者需要实现名为 NSKeyValueObserving 的 category 方法来处理收到的变更通知:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object chan ge:(NSDictionary *)change context:(void *)context;

在这里,change 这个字典保存了变更信息,具体是哪些信息取决于注册时 的 NSKeyValueObservingOptions。 

 

2 手动实现键值观察

  • 首先,需要手动实现属性的 setter 方法,并在设置操作的前后分别调用 willChangeValueForKey: 和 didChangeValueForKey 方法,这两个方法用于通知系统该 key 的属性值即将和已经变更了;
  • 其次,要实现类方法 automaticallyNotifiesObserversForKey,并在其中设置对该 key 不自动 发送通知(返回 NO 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理。
 1 - (void) setAge:(int)theAge
 2  {
 3     [self willChangeValueForKey:@"age"]; 
 4     age = theAge;
 5     [self didChangeValueForKey:@"age"];
 6 }
 7 
 8 + (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key 
 9 { 
10 if ([key isEqualToString:@"age"]) 
11 {
12     return NO; 
13 }
14     return [super automaticallyNotifiesObserversForKey:key];
15  }

3键值观察依赖键

有时候一个属性的值依赖于另一对象中的一个或多个属性,如果这些属性中任一属性的值发生变更,被依

赖的属性值也应当为其变更进行标记。因此,object 引入了依赖键。 

/*********************************************/

/******************待续*************************/

 

4.键值观察的实现

KVO 是通过 isa-swizzling 实现的。

 

当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。 

派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制

 

demo

1 Foo * anything = [[Foo alloc] init]; Foo * x = [[Foo alloc] init];
 2 Foo * y = [[Foo alloc] init];

3
?Foo * xy = [[Foo alloc] init]; 4 ?Foo * control = [[Foo alloc] init]; 5 6 [x addObserver:anything forKeyPath:@"x" options:0 context:NULL]; 7 [y addObserver:anything forKeyPath:@"y" options:0 context:NULL]; 8 [xy addObserver:anything forKeyPath:@"x" options:0 context:NULL]; 9 [xy addObserver:anything forKeyPath:@"y" options:0 context:NULL];

 

创建了四个对象,x 对象的 x 属性被观察,y 对象的 y 属性被观察,xy 对象的 x 和 y 属 性均被观察,参照对象 control 没有属性被观察。 

结果

如果使用对象的 -class 方面输出类名始终为:Foo,这是因为新诞生的派生类 重写了 -class 方法声称它就是起初的基类,(将被观察对象的isa 指向这个派生类)

只有使用 runtime 函数 object_getClass 才能一睹芳容: NSKVONotifying_Foo。 

x,y 以及 xy 三个被观察对象真正的类型都是 NSKVONotifying_Foo, 而且该类实现了:setY:, setX:, class, dealloc, _isKVOA 这些方法。其中 setX:, setY:, class 和

dealloc 前面已经讲到过,私有方法 _isKVOA 估计是用来标示该类是一个 KVO 机制声称的类。在这里 Objective C 做了一些优化,它对所有被观察对象只生成一个派生类,该派生类实现所有被观察对象的 setter 方法,这样就减少了派生类的数量,??供了效率。所有 NSKVONotifying_Foo 这个派生类重写 了 setX,setY 方法(留意:没有必要重写 setZ 方法)。 

 

参考资料:

KVO 和 KVC 的使用和实现 http://objccn.io/issue-7-3/ 

OC之KVC,KVO

标签:

原文地址:http://www.cnblogs.com/H7N9/p/4908224.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!