观察者模式本质上时一种发布-订阅模型,用以消除具有不同行为的对象之间的耦合,通过这一模式,不同对象可以协同工作,同时它们也可以被复用于其他地方Observer从Subject订阅通知,ConcreteObserver实现重现ObServer并将其重载其update方法。一旦SubJect的实例需要通知Observer任何新的变更,Subject会发送update消息来通知存储在其内部类中所注册的Observer、在ConcreteObserver的update方法的实际实现中,Subject的内部状态可被取得并进行后续处理。其类图如下:
观察者模式.png
由上面我们可以发现观察者模式无非在是定义对象间的一种一对多的依赖关系,并且当一个对象的状态发生改变的时候,所有依赖于它的对象都会得到通知且自动更新。即如果Subject允许其他观察者(实现了观察者接口的对象)对这个Subject的改变进行请阅,当Subject发送了变化,那么Subject会将这个变化发送给所有的观察者,观察者就能对Subject的变化做出更新。其时序图如下
观察者模式2.png
通过上面的观察我们可以发现如果用N个Observer来拓展Subject的行为,这些Observer具有处理存储在Subject中的信息的特定实现,这样也就实现了前面所说的消除不同对象间的耦合的功能了。
那么了解了这些我们可能就会更像了解下我们在什么时候才会去使用观察者模式呢?
- 当需要将改变通知所有的对象时,而你又不知道这些对象的具体类型
- 改变发生在同一个对象中,并需要改变其他对象将相关的状态进行更新且不知道有多少个对象。
而同样的在我们日常的开发中在Cocoa Touch框架中的的两种经常打交道的技术KVO与通知都实现了观察者模式,所以下面我们讨论的重点也就是基于这两个方面的。
通知
小编发布了一篇文章,地址是:http://www.jianshu.com/p/2efc71c4188a 里面有通知的基础用法,这里就不过多的介绍了。
KVO
KVO是Key-Value-Observing的缩写
通过KVO这种机制对象可以通过它得到其他对象的某个属性的变更通知。这种机制在MVC模式下显得更为重要,KVO可以让视图对象经过控制器观察模型对象的变更从而做出更新等操作。
KVO这一机制是基于NSKeyValueObserving协议的,Cocoa通过这个协议为所有遵循协议的对象提供了自动观察属性变化的能力。在NSObject中已经为我们实现了这一协议,所以我们不必去实现这个协议。
下图形象的表示了KVO的一种工作流程:
为什么要使用KVO?
有的朋友可能会有疑问,为什么要使用KVO呢?KVO能实现的我使用Setter方法同样能实现啊。其实不然KVO存在还是有它的价值的,那么接下来我们细数一下KVO的独特价值吧:
1.我们创建一两个setter方法感觉没什么,但是如果要观察的属性非常多,那么还能一一重写setter方法来实现吗?想必大家心里已有了答案,但是利用KVO则能很好的解决上述问题。
2.我们自定义的类是很容易改写setter方法的,但是如果你是用一个已经编译好了的类库时要监控其中一个属性时怎么办?难道还要去重写setter方法?如果使用KVO则很轻松解决问题。
3.使用KVO能够方便的记录变化前的值和变化后的值,不适用KVO你还要自己来解决这些问题。
4.KVO让你的代码看起来更加简洁清晰易于维护。
观察的是,属性是否执行了setter方法或者是是否使用了kvc赋值了,只要有赋值的动作,都会执行kvo的回调方法。如果赋值没有通过setter方法或者KVC,例如(_name = @"新值"),这个时候,不会触发kvo回调方法
一般KVO崩溃的原因
1.被观察的对象销毁掉了(被观察的对象是一个局部变量)2.观察者被释放掉了,但是没有移除监听 3.注册的监听没有移除掉,又从新注册了一遍监听
KVO使用方法
添加观察者和被观察者
//你要观察那个对象,就给那个对象添加一个观察者
//观察的是self.view 的backgroundColor属性 获取旧值和新值
//observer:观察者(观察self.view对象属性的变化)
//keypath:被观察属性的名称
//观察属性的新值,旧值等的一些配置(枚举值)
//content:上下文,可以为kvo的回调方法传值
//注册观察者(添加观察者)
[self.view addObserver:self forKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//观察Person类的对象的name属性
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
KVO回调方法
//keyPath:属性名称
//object:被观察的对象
//change:变化前后的值都存储在Change字典中
//context:注册观察者的时候content传递过来的值。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
//监听了好几个属性,使用keyPath区分
//监听了好几个view,通过object判断
//view的背景颜色发生了变化
if ([keyPath isEqualToString:@"backgroundColor"]) {
id oldColor = [change objectForKey:NSKeyValueChangeOldKey];
NSLog(@"旧值---%@---%@", NSKeyValueChangeOldKey, oldColor);
id newColor = [change objectForKey:NSKeyValueChangeOldKey];
NSLog(@"新值---%@---%@", NSKeyValueChangeNewKey, newColor);
}
//name属性发生变化
if ([keyPath isEqualToString:@"name"]) {
id oldName = [change objectForKey:@"old"];
NSLog(@"oldName == %@", oldName);
id newName = [change objectForKey:@"new"];
NSLog(@"newName == %@", newName);
}
}
最后使用完之后释放掉观察者 一般在dealloc 或者是viewDidDisappear:方法中释放掉观察者
//在视图已经消失,移除监听
- (void)viewDidDisappear:(BOOL)animated {
//移除监听
[self.view removeObserver:self forKeyPath:@"name" context:nil];
}
//在ARC下,不能使用[super delloc]
- (void)dealloc {
//移除监听
[self.person removeObserver:self forKeyPath:@"name" context:nil];
self.person = nil;
}
参考文章:http://blog.csdn.net/xdrt81y/article/details/24039163 作者:LoveApp_Han
http://www.jianshu.com/p/56d9417c3a04 作者:晓风沐晨 http://www.jianshu.com/users/350fee9655f0/latest_articles