Kvo是Cocoa的一个重要机制,他提供了观察某一属性变化的方法,极大的简化了代码。这种观察-被观察模型适用于这样的情况,比方说根据A(数 据类)的某个属性值变化,B(view类)中的某个属性做出相应变化。对于推崇MVC的cocoa而言,kvo应用的地方非常广泛。(这样的机制听起来类 似Notification,但是notification是需要一个发送notification的对象,一般是 notificationCenter,来通知观察者。而kvo是直接通知到观察对象。)
适用kvo时,通常遵循如下流程:
1 注册:
keyPath就是要观察的属性值,options给你观察键值变化的选择,而context方便传输你需要的数据(注意这是一个void型)
2 实现变化方法:
change:(NSDictionary
change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
是不是很简单?kvo的逻辑非常清晰,实现步骤简单。
说了这么多,大家都要跃跃欲试了吧。可是,在此之前,我们还需要了解KVC机制。其实,知道了kvo的逻辑只是帮助你理解而已,要真正掌握的,不在 于kvo的实现步骤是什么,而在于KVC,因为只有符合KVC标准的对象才能使用kvo(强烈推荐要使用kvo的人先理解KVC)。
KVC是一种间接访问对象属性(用字符串表征)的机制,而不是直接调用对象的accessor方法或是直接访问成员对象。
key就是确定对象某个值的字符串,它通常和accessor方法或是变量同名,并且必须以小写字母开头。Key path就是以“.”分隔的key,因为属性值也能包含属性。比如我们可以person这样的key,也可以有key.gender这样的key path。
获取属性值时可以通过valueForKey:的方法,设置属性值用setValue:forKey:。与此同时,KVC还对未定义的属性值定义了 valueForUndefinedKey:,你可以重载以获取你要的实现(补充下,KVC定义载NSKeyValueCoding的非正式协议里)。
在O-C 2.0引入了property,我们也可以通过.运算符来访问属性。下面直接看个例子:
instance.number
[instance setValue:[NSNumber numberWithInteger: 3 ] forKey: @" number " ];
注意KVC中的value都必须是对象。
以上介绍了通过KVC来获取/设置属性,接下来要说明下实现KVC的访问器方法(accessor method)。Apple给出的惯例通常是:
-key:,以及setKey:(使用的name convention和setter/getter命名一致)。对于未定义的属性可以用setNilValueForKey:。
至此,KVC的基本概念你应该已经掌握了。之所以是基本,因为只涉及到了单值情况,kvc还可以运用到对多关系,这里就不说了,留给各位自我学习的空间
接下来,我们要以集合为例,来对掌握的KVC进行一下实践。
之所以选择array,因为在ios中,array往往做为tableview的数据源,有这样的一种情况:
倘若使用了KVC/kvo,这样的麻烦就迎刃而解了,你将不用关心追加或是更新多少条数据。
下面将以添加数据为例,说明需要实现的方法:
实现insertObject:inKeyAtIndex:或者insertKey:atIndexes。同时在kvo中我们可以通过change这个dictionary得知发生了哪种变化,从而进行相应的处理。
-
1.person类 - @implementation
Person - @synthesize
name,age;//属性name 将被监视 - -(void)
changeName - {
-
name=@"changeName directly"; - }
- @end
-
-
- 2.PersonMonitor类
监视了name属性 - @implementation
PersonMonitor -
- -
(void)observeValueForKeyPath:(NSString *)keyPath -
ofObject:(id)object -
change:(NSDictionary *)change -
context:(void *)context - {
-
if ([keyPath isEqual:@"name"]) -
{ -
NSLog(@"change happen, old:%@ new:%@",[change objectForKey:NSKeyValueChangeOldKey],[change objectForKey:NSKeyValueChangeNewKey]); -
} - }
- @end
-
-
- 3测试代码
-
-
//初始化被监视对象 -
Person *p =[[Person alloc] init]; -
//监视对象 -
PersonMonitor *pm= [[PersonMonitor alloc]init]; -
[p addObserver:pm forKeyPath:@"name" options:(NSKeyValueObservingOptio nNew |NSKeyValueObservingOptio nOld) context:nil]; -
- //测试前的数据
-
NSLog(@"p.name is %@",p.name); -
- //通过setvalue
的方法,PersonMonitor的监视将被调用 -
[p setValue:@"name kvc" forKey:@"name"]; -
- //查看设置后的值
-
NSLog(@"p name get by kvc is %@",[p valueForKey:@"name"]); -
- //效果和通过setValue
是一致的 - p.name=@"name
change by .name="; -
-
//通过person自己的函数来更改name -
[p changeName]; -
-
结果是 - 输出
- 2011-07-03
16:35:57.406 Cocoa[13970:903] p.name is name - 2011-07-03
16:35:57.418 Cocoa[13970:903] change happen, old:name new:name kvc - 2011-07-03
16:35:57.420 Cocoa[13970:903] p name get by kvc is name kvc - 2011-07-03
16:35:57.421 Cocoa[13970:903] change happen, old:name kvc new:name change by .name= - 最后一次修改是直接修改
所以没法产生通知
二
最后一个是直接修改数据不是kvc 模式修改数据,所以不会检测到数据的变化,即打印出数据的变化。
2.2问题
- 1 p.name=@"name
change by .name=";
2 [p setValue:@"name kvc" forKey:@"name"];