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

百恒网络

南昌百恒网络

ios设计模式之委托模式

百恒网络 2017-03-28 220

今天,让我们跟随昨天的步伐,和南昌APP开发公司--百恒网络一起学习ios设计模式之委托模式。

委托模式

委托模式从GoF装饰(Decorator)模式、适配器(Adapter)模式和模板方法(Template Method)模式等演变而来。几乎每一个应用都会或多或少地用到委托模式。不只是Cocoa Touch框架,在Cocoa框架中,委托模式也得到了广泛的应用。

2.1 问题提出

对于应用生命周期的非运行状态应用启动场景,我们把从点击图标到启动第一个界面的过程细化了一下,具体如图所示。

假设这一系列的处理都是在上帝类 UIApplication 中完成的之所以叫“上帝类”(god class),是因为它“无所不能”、“包含所有”。 在面向对象的软件设计中,“上帝类”不是很友好,需要重构。在编程的过程中,要尽量避免使用上帝类,因为上帝类是高耦合的,职责不清,难以维护。我们需要“去除上帝类”,把看似功能很强且很难维护的类,按照职责将它的属性或方法分派到各自的类中或分解成功能明确的类。

幸运的是,苹果没有把 UIApplication 类设计成“上帝类”,而是将它们分割到两个不同的角色类中:其中一个扮演框架类角色,框架类具有通用、可重复使用、与具体应用无关等特点;另一个扮演应用相关类的角色,应用相关类与具体应用有关。由于受到框架类的控制,应用相关类常常被设计为“协议”,在Java中称为“接口”。开发人员需要在具体的应用中实现这个“协议”。

如图所示,将一些功能提取出来放在application:didFinishLaunchingWithOptions: 和 applicationDidBecomeActive: 方法中完成,定义在UIApplicationDelegate 协议中,这样 UIApplication 类就变成了框架类。

非运行状态应用启动场景的流程图

非运行状态应用启动场景的流程图

去“上帝”化的非运行状态启动场景流程图

去“上帝”化的非运行状态启动场景流程图

在具体使用时,需要实现 UIApplicationDelegate 协议。HelloWorld应用的类图如图所示。

去“上帝”化的HelloWorld应用类图

去“上帝”化的HelloWorld应用类图

UIApplication 不直接依赖于 AppDelegate 类,而是依赖于 UIApplicationDelegate 协议,这在面向对象软件设计原则中叫做“面向接口的编程”。 AppDelegate 类实现协议UIApplicationDelegate ,它是委托类。

委托是为了降低一个对象的复杂度和耦合度,使其能够更具通用性而将其中一些处理置于委托对象中的编码方式。通用类因为通用性(与具体应用的无关性)而变为框架类,框架类保持委托对象的指针,并在特定时刻向委托对象发送消息。消息可能只是通知委托对象做一些事情,也可能是对委托对象进行控制。

2.2 实现原理

下面我们通过一个案例介绍委托设计模式的实现原理和应用场景,重新绘制的委托设计模式类图如图所示。

委托设计模式类图

委托设计模式类图

古希腊有一位哲学家,他毕生只做三件事情:“睡觉”“吃饭”和“工作”。为了更好地生活,提高工作效率,他会找一个徒弟,把这些事情委托给徒弟做。然而要成为他的徒弟,需要实现一个协议,协议要求能够处理“睡觉”“吃饭”和“工作”的问题。三者的关系如类图所示。

委托设计模式哲学家案例类图(左图为Swift版,右图为Objective-C版)

委托设计模式哲学家案例类图(左图为Swift版,右图为Objective-C版)

从图中所示的哲学家类图中可以看到,通用类( Philosopher )就是哲学家,它通过 delegate 属性保持委托对 象 ( ViewController ) 的 引 用 , 委 托 对 象 ( ViewController ) 就 是 哲 学 家 的 徒 弟 , 它 实 现 了 协 议PhilosopherDelegate 。 PhilosopherDelegate 规定了3个方法: sleep 、 eat 和 work 方法。

下面我们看看实现代码,委托协议PhilosopherDelegate的代码如下:

委托协议PhilosopherDelegate的代码

可以看到,委托协议 PhilosopherDelegate 定义了3个方法。它的实现类是 ViewController ,相关代码如下:

ViewController

委托对象如何与通用类建立引用关系呢?这通过 viewDidLoad 方法中的 tom.delegate = self 语句来指定。一般情况下,通用类由框架直接提供。在这个例子中,我们根据需要自己实现通用类 Philosopher ,相关代码如下:

我们根据需要自己实现通用类Philosopher

在上述代码中,第①行用于定义 delegate 属性。

比较说明 在Swift版本中, delegate 属性的类型是 PhilosopherDelegate? ,它可以保存委托对象的引用,其中 ?号表示 delegate 可以为 nil 。在Objective-C版本中, delegate 属性的类型是 id ,它可以保存委托对象的引用,其中属性 weak 说明是“弱引用”。这里使用弱引用方式是为了防止内存

引用计数增加而导致委托对象无法释放的问题。

为了测试我们在 Philosopher 中通过 NSTimer 每3秒发出一个,依次向委托对象发出消息 sleep 、 eat 和 work 。self.delegate 事实上是 ViewController 对象,所以第②行代码调用 ViewController 中的 sleep 方法。

2.3 应用案例

我们以 UITextFieldDelegate 为例来说明一下委托的用法。 UITextFieldDelegate 是控件UITextField的委托,它主要负责响应控件事件或控制其他对象。除了UITextField,WebView和UITableView等控件也有相应的委托对象。

打开 UITextFieldDelegate 的API文档(如图所示),可以发现其中有4个与编辑有关的方法,还有3个其他方法。

UITextFieldDelegate 的API文档

UITextFieldDelegate 的API文档

这里我们重点介绍在编辑过程中消息的发送以及UITextField控件与 UITextFieldDelegate 委托对象之间的交互过程,如图所示。

UITextField控件与 UITextFieldDelegate 委托对象之间的交互过程

在文本框开始编辑前后,会分别发出消息textFieldShouldBeginEditing: 和textFieldDidBeginEditing: ,编辑结束前后会分别发出消息textFieldShouldEndEditing: 和 textFieldDidEndEditing: 。

注意 委托消息命名有一定的约定性,如果是UITextField发出的消息,就以 textField 开头,后面跟3个词之一—— Should 、 Will 或 Did 。在使用 Should 消息时,应该返回一个布尔值,这个返回值用于确定委托是否会响应消息;当使用 Will 后缀时,没有返回值,表示改变前要做的事情;当使用 Did 后缀时,也没有返回值,表示改变之后要做的事情。这3种方法都会把发送消息的对象以参数的形式回传回来,例如textFieldShouldBeginEditing(textField: UITextField) 消息中的参数 textField 。

为了演示文本框编辑前后发生了什么,我们需要编写一个简单的文本框工程,如图所示,其中界面中只包含一个文本框,然后为文本框定义输出口。最后添加Auto Layout所有约束。

文本框工程界面

文本框工程界面

我们在视图控制器 ViewController 中实现 UITextFieldDelegate ,相关代码如下:

视图控制器ViewController中实现UITextFieldDelegate

视图控制器ViewController中实现UITextFieldDelegate

在 viewDidLoad 方法中,第①行代码极为重要,是将当前视图控制器分配给文本框委托对象。除了通过编程实现分配委托对象外,我们也可以通过Interface Builder在故事板中连线分配委托对象。如图所示,打开故事板文件,右击文本框控件,从弹出的快捷菜单中,将位于Outlets(输出口)下面的delegate后面的圆圈用鼠标拖曳到View Controller上,然后释放鼠标。

定义委托输出口

定义委托输出口

运行代码,输出的日志如下:

运行代码,输出的日志

输入完成后,点击return键,关闭键盘,结束编辑状态,此时日志中的输出结果如下:

点击return键,关闭键盘,结束编辑状态,此时日志中的输出结果

其中 textFieldShouldReturn: 是点击return键时发出的消息,我们借助该消息通过 textField.resignFirst-Responder() 方法关闭键盘。

对于一些更复杂的控件(如UITableView),除了需要实现委托协议外,还需要实现数据源协议。数据源与委托一样,都是委托设计模式的具体应用,委托对象主要对控件对象的事件和状态变化作出响应,而数据源对象是为控件对象提供数据。

本文仅限内部技术人员学习交流,不得作于其他商业用途.希望此文对广大技人员有所帮助。文章出自:南昌APP开发公司-百恒网络。如有需要,百恒网络欢迎随时咨询。

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

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

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