十年专注于品牌网站建设 十年专注于品牌网站建设,低调、有情怀的网络应用服务商!
南昌百恒网络微信公众号 扫一扫关注
小程序
tel-icon全国服务热线:400-680-9298,0791-88117053
扫一扫关注百恒网络微信公众号
扫一扫打开百恒网络微信小程序

百恒网络

南昌百恒网络

ios设计模式之观察者模式

百恒网络 2017-04-05 614

上周我们跟随南昌APP开发制作公司--百恒网络一起学习了ios设计模式之常用模式和委托模式,今天我们继续来学习ios设计模式之观察者模式。

观察者(Observer)模式也叫发布/订阅(Publish/Subscribe)模式,是 MVC( 模型-视图-控制器)模式的重要组成部分。

3.1 问题提出

天气一直是英国人喜欢讨论的话题,而最近几年天气也成为中国人非常关注的话题。我会根据天气预报决定是坐地铁还是开车上班,也会根据天气预报决定明天穿哪件衣服。于是在移动公司为我的手机定制了天气预报短信通知服务,它的工作模型如图所示。

定制天气预报短信通知服务

定制天气预报短信通知服务

每天气象局将天气预报信息投送给移动运营商,移动运营商的短信中心负责把天气预报发送给定制过这项服务的手机。

在软件系统中,一个对象状态的改变也会连带影响其他很多对象的状态发生改变。能够实现这一需求的设计方案有很多,但能够做到复用性强且对象之间匿名通信的,观察者模式是其中最为适合的一个。

3.2 实现原理

观察者模式的类图如图所示。 它有4个角色,具体如下所示。

抽象主题( Subject )。抽象主题是一个协议,它是一个观察者集合容器,定义了添加观察者( attach )方法、移除观察者( detach )方法和为所有观察者发送通知的方法( notifyObserver )。

抽象观察者( Observer )。抽象观察者也是一个协议,它有一个更新( update )方法。

具体观察者( ConcreteObserver )。 Observer 协议的具体实现。

具体主题( ConcreteSubject )。 Subject 协议的具体实现。

引入 Subject 和 Observer 这两个协议后,不仅提高了系统的可复用性,还降低了耦合度。观察者模式还可以有其他变形,若要深入了解,可以参考GoF。

观察者模式的类图(上图为Swift版,下图为Objective-C版)

观察者模式的类图(上图为Swift版,下图为Objective-C版)

3.3 通知机制和 KVO 机制

在Cocoa Touch框架中,观察者模式的具体应用有两个——通知(notification)机制和KVO(Key-ValueObserving)机制,下面简要介绍这两种机制。

1. 通知机制

通知机制与委托机制不同的是,前者是“一对多”的对象之间的通信,后者是“一对一”的对象之间的通信。

说明:在iOS中,通知一词多次出现过,归纳一下主要有广播通知(broadcast notification)、本地通知(localnotification)和推送通知(push notification),本节介绍的是广播通知。事实上,除了名字相似,广播通知与其他两个通知完全不同:广播通知是Cocoa Touch框架中实现观察者模式的一种机制,它可以在一个应用内部的多个对象之间发送消息;本地通知和推送通知中的“通知”是给用户一种“提示”,它的“提示”方式有警告对话框、发出声音、振动和在应用图标上显示数字等。 在计划时间达到时,本地通知由本地iOS发出。推送通知由第三方程序发送给苹果的远程服务器,再由远程服务器推送给iOS的特定应用。

如图所示,在通知机制中对某个通知感兴趣的所有对象都可以成为接收者。首先,这些对象需要向通知中心( NSNotificationCenter )发出addObserver:selector:name:object: 消息进行注册,在投送对象投送通知给通知中心时,通知中心就会把通知广播给注册过的接收者。所有的接收者都不知道通知是谁投送的,更不关心它的细节。

投送对象与接收者是一对多的关系。接收者如果对通知不再关注,会给通知中心发出 removeObserver:name:object:消息解除注册,以后不再接收通知。

通知机制图

通知机制图

下面我们介绍一下通知机制的使用过程。这里我们将7.1节的模态视图案例重新设计一下,图8-15所示,在注册视图点击Save按钮返回到登录视图时,把数据回传给登录视图。

模态视图案例

模态视图案例

登录视图控制器 ViewController 作为通知的接收者,注册视图控制器 RegisterViewController 作为通知投送对象,如图所示。

通知机制图

通知机制图

在ViewController视图控制器中,注册通知接收者的代码如下:

在ViewController视图控制器中,注册通知接收者的代码

NSNotificationCenter 是单例模式,创建和获得共享实例的方法是 defaultCenter , NSNotificationCenter 的addObserver:selector:name:object:方法能够 注册通知。当接收到 AppWillTerminateNotification 通知时,就会调用handleTerminate: 方法。

解除注册代码也类似,通过 NSNotificationCenter 发出 removeObserver 消息实现。对于视图控制器,也可以在didReceiveMemoryWarning 方法中发出消息,具体代码如下:

在didReceiveMemoryWarning 方法中发出消息,具体代码

ViewController中接收通知的方法是 registerCompletion: ,其代码如下:

这个方法可以接收一个 NSNotification类型 的参数。NSNotification 类中有3个重要的属性: name 、 object 和userInfo ,这3个属性与通知中心投送方法中的参数有一定的对应关系,如图所示。

NSNotification 类和通知中心中投送方法参数的关系

NSNotification 类和通知中心中投送方法参数的关系

其中 name 是通知的名字, object 是投送通知时传递过来的对象, userInfo 是投送通知时定义的字典对象,可借助于该参数传递数据。

在 RegisterViewController 视图控制器中,投送通知的代码如下:

在 RegisterViewController 视图控制器中,投送通知的代码

NSNotificationCenter 的投送方法除了代码中所示外,还有另外两个重载方法:

NSNotificationCenter 的投送方法

它们可以投送不带 userInfo 参数的通知,我们可以根据需要进行选择。还要注意的是, object 参数未必是 self对象,我们可以根据需要传递一个对象,如果接收者不需要,可以将其设为 nil 。

当我们运行代码,从注册视图进入登录视图后,会在日志中输出回传回来的 username 参数。

Cocoa和Cocoa Touch框架都提供一些通知,由系统自动投送。现在修改 ViewController 类添加系统通知:

修改 ViewController 类添加系统通知

修改 ViewController 类添加系统通知

在 viewDidLoad 方法中,第①~②行代码是系统注册通知 UIApplicationDidEnterBackgroundNotification (进入到后台通知)和:

UIApplicationWillEnterForegroundNotification (回到前台通知)。第③~④行代码是接收通知后的事件处理。

提示 在iOS设备上可以按Home键进入后台,快速按两次Home键可以打开最近使用应用列表,点击应用可以使该应用重新回到前台。另外,在Xcode模拟器中没有Home键,点击Home键的操作可以通过组合键command+shift+H完成。

除了应用生命周期的不同阶段有不同的通知外,很多控件也会在某些事件发生时投送通知,例如UITextField控件。在编辑过程的不同阶段,UITextField控件会分别发出如下通知:UITextFieldTextDidBeginEditingNotification 、 UITextFieldTextDidChangeNotification 和 UITextFieldTextDidEndEditingNotification 。

2. KVO机制

KVO不像通知机制那样通过一个通知中心通知所有观察者对象,而是在对象属性变化时通知会被直接发送给观察者对象。图为KVO机制解析图。

可以看到,属性发生变化的对象需要发出消息 addObserver:forKeyPath:options:context: 给注册观察者,使观察者关注它的某个属性的变化。当对象属性变化时,观察者就会接收到通知,观察者需要重写方法 observeValueForKeyPath:ofObject:change:context: 以响应属性的变化。

KVO机制图

KVO机制图

下面我们来看一个实际的案例。我们使用KVO机制来监视应用程序的状态变化。应用程序委托对象

AppDelegate 的 appStatus 属性是要观察的属性。 AppDelegate 的代码如下:

AppDelegate的代码

上述代码中第①行的 appStatus 属性是需要观察的属性,在Swift版中它的定义上必须要加 dynamic,以表示该属性是在运行时动态派发的。第②行代码用于定义观察者AppStatusObserver , AppStatusObserver 是我们的自定义类,它负责观察 appStatus 属性的变化。第③行代码用于创建 AppStatusObserver 对象。

第④行代码是关键,addObserver:forKeyPath:options:context: 语句告诉观察者( AppStatusObserver )开始观察 AppDelegate 的 appStatus 属性变化,其中参数 addObserver 是观察者对象; forKeyPath 是被关注对象的属性;options 是为属性变化设置的选项,本例中 New 和 Old 表示把属性新旧两个值都传递给观察者,这些值是NSKeyValueObservingOptions 类型的成员; context 参数是上下文内容,它的类型是C语言形式的任何指针类型,

Swift版表示为 UnsafeMutablePointer ,Objective-C版本表示为 void * 。

观察者 AppStatusObserver 的代码如下:

观察者 AppStatusObserver 的代码

因为 NSObject 类实现了 NSKeyValueObserving 协议,所以只需声明 AppStatusObserver 继承了 NSObject 类,而无需实现 NSKeyValueObserving 协议。

observeValueForKeyPath:ofObject:change:context: 方法的 observeValueForKeyPath 参数是被关注的属性。

ofObject 是被关注的对象, change 是字典类型,包含了属性变化的内容,这些内容与注册时属性变化设置的选项( options 参数)有关。 context 是注册时传递的上下文内容。

第一次运行程序到界面时,会有两个状态的变化,日志结果如下:

第一次运行程序到界面时,会有两个状态的变化

如果将应用退到后台,然后再回到前台,日志结果如下:

如果将应用退到后台,然后再回到前台

关于应用程序状态变化相关的内容,这里不再解释。

本文仅限内部技术人员学习交流,不得作于其他商业用途.希望此文对广大技人员有所帮助。文章出自:南昌APP开发制作公司--百恒网络:http://www.jxbh.cn

400-680-9298,0791-88117053
扫一扫关注百恒网络微信公众号
扫一扫打开百恒网络小程序

欢迎您的光顾,我们将竭诚为您服务×

售前咨询 售前咨询
 
售前咨询 售前咨询
 
售前咨询 售前咨询
 
售前咨询 售前咨询
 
售前咨询 售前咨询
 
售后服务 售后服务
 
售后服务 售后服务
 
备案专线 备案专线
 
×