iOS观察者模式的底层实现
KVO(Key Value Observing, 键值观察)是一种设计模式,是Objective-C对观察者模式的实现,每次当被观察对象的某个属性值发生改变时,注册的观察者便能获得通知。
使用KVO很简单,分为三个基本步骤:
①、注册观察者,指定被观察对象的属性:
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
通知其他对象的方法,这个方法在NSObject中就已经申明了,也就是说任何继承自NSObject的对象都可以使用KVO.
②、在观察者中实现以下回调方法:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(voidvoid *)context
{
// use the context to make sure this is a change in the address,
// because we may also be observing other things
NSString *name = [object valueForKey:@"name"];
NSLog(@"new name is: %@", name);
}
只要person对象中的name属性发生变化,系统会自动调用该方法。
③、最后,不要忘记在dealloc中移除观察者
-(void)dealloc
{
// must stop observing everything before this object is
// deallocated, otherwise it will cause crashes
for(Person *p in m_observedPeople){
[p removeObserver:self forKeyPath:@"name"];
}
[m_observedPeople release];
m_observedPeople = nil;
}
注意:添加的观察者的次数要和移除观察者的次数相等,少移除一个或者多移除一个都会造成程序崩溃:
1、KVO的实现原理:
当设置一个类为观察对象时,系统会动态地创建一个新的类,这个新的类继承自被观察对象的类,还重写了基类被观察属性的setter方法。派生类在被重写的setter方法中实现真正的通知机制。最后,系统将这个对象的isa指针指向这个新创建的派生类,这样,被观察对象就变成了新创建的派生类的实例。(注:runtime中,对象的isa指针指向该对象所属的类,类的isa指针指向该类的metaclass。有关OC的对象、类对象、元类对象metaclass object和isa指针,请戳这里详细了解)。同时,新的派生类还重写了dealloc方法(removeObserver)。
2、KVO的缺陷
不可否认,KVO的功能确实很强大,但是它的缺点也很明显:
①、过于简单的API
KVO中只有通过重写-observeValueForKeyPath:ofObject:change:context方法来获取通知,该方法有诸多限制:不能使用自定义的selector,不能使用block,而且当父类也要监听对象时,往往要写一大坨代码。
②、父类和子类同时存在KVO时(监听同一个对象的同一个属性),很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash。要避免这个问题,就需要区分出KVO是self注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。