IOS设计模式-观察者模式
前言:23种软件设计模式中的观察者模式,也是在软件开发中,挺常用的一种设计模式。而在苹果开发中,苹果Cocoa框架已经给我们实现了这个设 计模式,那就是通知和KVO(Key-Value Observing),本篇博文将会先讲解通知和KVO的常用方法和使用示例,然后讲解观察者模式以及对观察者模式的实现。
文章内容大纲:
1、KVO的使用
2、通知的使用
3、观察者模式
正文:
1、KVO的使用
addObserver:forKeyPath:options:context:
通知其他对象的方法,这个方法在NSObject中就已经申明了,也就是说任何继承自NSObject的对象都可以使用KVO.
我们来实现一个对象a值改变的时候去通知对象b,也就是说这里b就是a的观察者,b观察a的变化,然后做出相应的反应.
Model_A.h和Model_A.m:
Model_B.h和Model_B.m:
最后在ViewController.h中:
输出结果:
2016-03-10 18:02:39.955 KVO[24772:539854] 程序开始执行!
2016-03-10 18:02:41.959 KVO[24772:539854] Model_B
-----------------------------------------------------------------------------------------------------------------------------------------------------------
如果注释掉Model_B中的方法observeValueForKeyPath:ofObject:change:context:,运行时会导致崩溃:
也就是这么一层关系:
A对象要通知B对象,B对象必须实现监听的方法,否则一旦有消息发送就会导致崩溃.
A对象不想通知B对象了,需要从B对象身上移除掉通知.
要想程序不出现问题,我们需要实现3步.
(主动添加B的通知) A -------> B(不实现一个方法就崩溃)
(主动移除B的通知) A ---X--> B
(重复移除B的通知) A ---X--> B(崩溃)
用起来很恶性,还好,我们可以重复添加B的通知而不崩溃......
但是要注意:添加的观察者的次数要和移除观察者的次数相等,少移除一个或者多移除一个都会造成程序崩溃:
2、 通知的使用
Model.h和Model.m
简易封装的通知中心类:Notification.h 和 Notification.m
在ViewController.m中:
输出结果:
2016-03-10 20:36:20.077 通知[28333:641372]
Hello,NSConcreteNotification 0x7fbafbd0b620 {name = A; userInfo = Hello,a}!
2016-03-10 20:36:20.078 通知[28333:641372]
Hello,NSConcreteNotification 0x7fbafbd0bca0 {name = B; userInfo = Hello,b}!
3、观察者模式
关于观察者模式,我们百度百科一下,会找到:
观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
在这个图示例中,"订阅书刊的用户"需要向"书刊发行机构"提供基本的注册信息,这些信息包括用户自己的基本信息和要"订阅的书刊号",然后确定成为" 书刊发行机构"的用户,然后当"书刊发行机构"开始发行书刊,就会根据"订阅书刊的用户"的已经提供过的基本信息,进行发行书刊。然后"订阅书刊的用户" 就会得到相应的书刊。
在这个"发行-订阅"的模式中,"书刊发行机构"和"订阅书刊的用户"之间是靠"订阅书刊的用户"提供的基本有效的信息建立联系的。
这个"发行-订阅"的模式也是一种观察者模式的一个生活中非常生动的例子。"书刊发行机构"相当于观察者模式中的一个"通知中心"的角色,会根据实际需求根据用户信息向用户发送消息,然后用户作为观察者收到这些消息之后,就会自动的根据消息来执行自己的业务逻辑。
接下来,我们要实现通知中心的抽象设计
根据需求分析,首先我们需要一个唯一的"书刊发行机构",然后可以增加和删除书刊,同时能存储多个书刊,然后每个书刊又可以对应多个"订阅书刊的用户",
另外要补充一个就是,在添加和移除用户的接口中,我们需要传入用户信息对象,这个用户信息对象我们需要用协议来约束它,我们思考一下,比如订阅书刊,我 们当然需要用户最基本的两个信息,用户的ID和用户订阅的书刊号,用户的ID用于"书刊发行机构"能够准确的发消息或者发书刊正确无误的发对了人,这个作 用就好比我们唯一的身份证号码的作用是一样的。然后用户订阅的书刊号,就能保证"书刊发行机构"能把正确的书刊发到用户手中,不能随便什么书刊都发向随便 什么人的吧。
所以先写出下面所有的公开的接口,后面再具体实现它们,这就是面向接口设计:
另外,当"书刊发行机构"有了新的书刊的时候,根据需求,创建一个书刊号,然后如果有新的用户需要订阅新的书刊,根据需求,创建一个用户订阅书刊的信息对象。
一个书刊对应每个月会在某一天发布新书,当"书刊发行机构"的现有的书刊有了新的书发布的时候,就会发消息通知订阅这个书刊的用户,然后用户会做出及时的响应回馈。
按照观察者模式,被观察者发送消息,观察者收到消息就需要立马做出响应。这个和我们之前用的Cocoa框架已经实现的KVO和通知的模式是一样的。
为了实现这样的模式,我们还需要给用户信息对象用一个协议来约束它:
接着我们为上面的公开的接口具体实现,但是我们需要选择一个数据结构来存储书刊号和订阅书刊的用户信息对象,这里我选择使用NSMutableDictionary的Key-Value来存储书刊号和书刊,用NSHashTable来存储用户信息对象。
那么为什么要用NSHashTable来存储用户信息呢?为什么不用别的数据结构,比如数组(NSMutableArray)或者字典(NSMutableDictionary).
因为NSHashTable会对添加进的对象持有弱引用,当添加进来的元素,外部对他持有的强引用都取消的时候,这个对象就会自动从内存中消除,这样就不需要手动移除这些对象。
这个相比Cocoa给我们实现的KVO和通知,就不需要我们手动去操作实现removeObserve方法了。
虽然说,在使用NSHashTable实现的观察者模式,观察者对象本身就至少需要一个强引用,但是在实际开发中,观察者往往都至少会有一个强引用的。
所以,使用NSHashTable对系统算是一个小小的优化。
然后接着我们使用前面创造的观察者模式:
CustomerA.h和CustomerA.m:
在ViewController中,同时也把ViewController作为用户对象,以self传入:
打印:
2016-03-11 14:50:38.980 ObserverPattern[7958:175895] hashTable:NSHashTable {
[5] <ViewController: 0x7f93d2d26ac0>
[12] <CustomerA: 0x7f93d2c17f40>
}
2016-03-11 14:50:38.981 ObserverPattern[7958:175895] V1.0 SCIENCE
2016-03-11 14:50:38.981 ObserverPattern[7958:175895]
Hello,My Name is : CustomerA
V1.0--SCIENCE
备份的源码链接: http://pan.baidu.com/s/1eQTnm50 密码: s7su
4、自己实现KVO(以后有时间补上)
转载注明出处:http://www.cnblogs.com/goodboy-heyang/p/5265675.html ,尊重劳动成果。
本人最大的学习来源是阅读一直走在行业里很前面的大牛写的博客,有的博客还可能是一个团队写的,本人本篇深入学习的知识都是来自阅读它们的文章,中国有传统美德叫饮水思源,所以在这里也分享分享与本篇知识相关的学习资料来源(附有超链接,点击就能跳转):
1、《KVO的使用》
2、《通知中心NSNotificationCenter的使用》
3、《3种方式实现KVO并进行对比》
4、《手动设定实例变量的KVO实现监听》
5、《如何自己手动写一个KVO》
6、《需要罗列出所有的KVO的使用方法》