KVO和KVC分别是Key-Value Observing和Key-Value Coding的简称。想到这个话题是因为现在我写的tableView的更新是每隔数秒的轮询,在考虑是不是需要用个类似触发更新的机制。这自然就想到了观察者模式。
搜索Objective-c的观察者模式,发现在http://stackoverflow.com/questions/165790/whats-the-nicest-way-to-do-observer-observable-in-objective-c-iphone-version 中提到有两种方式可以实现:NSNotificationCenter和KVO。那个回答里形容Notification是较为古老而重型的。。。虽然我也稍微研究过,不过还是作为下次的话题吧。
1. KVC
咦,不是要说KVO吗?为什么先要说KVC?Mac Developer Library说KVC是KVO的准备知识。好吧,先来看看KVC。
KVC是一种非直接访问对象属性的机制。什么是直接访问?get和set。
用省略过头的话来解释,KVC就是通过key来获取value。
有点像Dictionary?嗯,从某种意义上来说是,但远远不止。
在Objective-C下,也有一个字典类NSDictionary,可以通过objectForKey方法来获取某个key所对应的value。但KVC可以在所有类中使用valueForKey这个方法,来获取某个property(key)的值(value)。
来看看代码范例。例如有这么一个类:
@interface TestClass : NSObject @property NSInteger integerProperty; @property TestClass *linkedInstance; @end
可以使用传统的set来赋值,例如:
TestClass *anotherInstance = [[TestClassalloc] init]; myInstance.linkedInstance = anotherInstance; myInstance.integerProperty = 2;
不过也可以用kvc来赋值:
TestClass *anotherInstance = [[TestClass alloc] init]; myInstance.linkedInstance = anotherInstance; [myInstance setValue:@2forKeyPath:@"linkedInstance.integerProperty"];
从这个角度上来说,KVC类似于.NET中反射的部分功能。
如上所述,要通过KVC取值,可以通过valueForKey。
由于是语言级别的功能,所以KVC能实现的功能挺强大。例如:
根据index来获取一个或数个:objectAtIndex, objectsAtIndexes, count, -get<Key>:range:
增删改:insertObject, insertObjects,removeObjectAtIndex, replaceObjectAtIndex,
2. KVO
KVO的作用是允许某个对象得知其他对象的某个属性发生了改动。在MVC的model和controller层之间的交互中应用得特别广泛。
实现KVO需要三步:
1) 明确通知对象和被通知对象
例如:如果存款发生了变更,比如被提取了5000元,那么账户所有人就需要被告知。这个场景中,通知对象就是银行账户,被通知对象就是账户所有人
2) 被通知对象在通知对象中,注册对某个属性的观察者
语法为:addObserver:forKeyPath:options:context:
对于上面那个例子,所有者就需要在账户实例里注册一个观察者。
代码范例:
[account addObserver:inspector
forKeyPath:@"openingBalance"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
其中option的参数表示需要得知这个属性更改前和更改后的值。
另外,可以添加,自然也可以用removeObserver移除。
3) 被通知对象(观察者)实现observeValueForKeyPath方法
这个方法是用来实现当被通知后,观察者会做些啥反应
对于上面那个例子,如果观察者判断自己有没有进行过提款或购物操作,然后决定是不是报警?
代码范例:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"openingBalance"]) { [openingBalanceInspectorField setObjectValue: [change objectForKey:NSKeyValueChangeNewKey]]; } /* Be sure to call the superclass's implementation *if it implements it*. NSObject does not implement the method. */ [superobserveValueForKeyPath:keyPath ofObject:object change:change context:context]; }
参考资料:
Key-Value Coding Programming Guide
Key-Value Observing Programming Guide