一、代理模式
代理模式(Delegate)即委托模式,不仅在应用开发中经常使用,而且是iOS SDK设计中常见的一种设计模式,例如UIKit框架中UI的交互逻辑通常需要开发人员自定义代理类去处理。
开发中代理模式常见用途
- 传值
- 功能分化接口设计
- 交互接口(自定义控件)
二、理解
撇开学科概念,代理俩字的意思是:一方帮助另一方代为处理(完成)一些工作或者任务。作为名词,则这个“一方” 就叫代理。如此理解,代理也可为独立的一者。
那么在开发这里,开始应该是这样:
类A引用着一个类B对象,类A中调用类B对象方法,那么类B对象就是一个代理,它帮A完成一些工作。 逻辑上A与B可能是整体与部分关系,也可能是平行关系,所以在设计中尽可能的封装类,实现功能分化。 因为代理是第三者,在代码中表现为一个类的对象,本质是一个类型对象的指针。
既然代理是实体对象, 如果类C、类D..与类B预备同样的功能约束,那么它们就可以取代类B成为类A的代理,即类型也可以是不同类型但得有一定约束。在开发中,如果A不确定要哪个做代理,通常将这个第三者代理对象泛指,就是指向谁,谁就做A的代理。代码中表现为一个id对象,本质是一个泛型指针,即可指向B/C,也可指向D。光是声明为id对象还不够,还要求C和D具有相同的功能,在代码中,这句话表现为Protocol(即协议,Java中为接口)。
这就是说各个类A的对象可以指定不同类型的对象作为它自己的代理。
三、总结
将以上所说A想成是功能受众,即B、C、D完成一些功能后,结果由A承受。
这种应用常见于回调传值和功能分化接口设计。其实是一个意思:将具体部分的功能分化成B/C/D,A在执行过程中,调用B/C/D完成一些功能,完成后将结果作用于A。传值在界面交互之间用的比较多(Block 也通常被用来传值,和代理传值统称为回调传值)。
但如果将上所说A想成是功能复用的,B/C/D反而是功能受众。则更能体现iOS SDK中代理模式的出神入化。比之如UIKit框架许多View的设计都是采用代理模式,如UIAlertView/UIActionSheet、UITableView/UICollectionView,UIPickerViewController等等。
它们有些共同点:
- 它们可以交互
- 而且他们的代理通常设置成当前View Controller
- 它们内部的功能都是复用的
- 设置代理目的都是将功能作用于View Controller
在项目中View Controller繁多,就相当于B/C/D,这些View就是A。它们采用的都是代理模式与View Controller交互,只不过View Controller才是功能受众。
所以在自定义控件的时候,可以多些考虑用代理模式设计控件用户交互。
四、多代理
以上所述代理是一对一的模式,比较简单,当然在设计中可能存在多层代理,对象之间是以分层结构,A为B代理,B又为C的代理,或者反过来理解,B需要从C承受一些东西,A又从B承受一些东西,但实际上还是属于一对一的代理结构
但在这里还有这种结构,就是多代理,一对多的结构。比如,在Socket编程中,通常Socket对象只有一个,是各种复杂结构的远程数据的接口,然而这些数据是分不同处理的,这些处理就有不同的模块去封装,那么数据要想到达对应的模块,就需要Socket对象或者是最上层的接受数据的对象把接受到的数据分发到各个模块,让各个模块决定是否处理该数据。典型的例子就是XMPPFramework框架,通过一个XMPPStream对象接收数据,后交给各个Extension去处理,Roster处理通讯录数据,Reconnect处理连接数据等等。
XMPPFramework 是目前比较强大的 iOS 客户端即时聊天的开源第三方框架, 采用的是XMPP协议。这个框架中,与服务器的交互依赖于XMPPStream对象,包括连接,聊天等。就是说所有类型数据都是从这个对象中进来。
IM应用中的好些功能,比如好友、群组、重连、缓存,在XMPPFrameWork框架中以扩展的形式存在,它们表现成一个个的类,它们的数据包形式各不相同。XMPPStream的设计巧妙的采用了代理模式,让各个扩展对象成为它的代理,这样一来,只要XMPPStream接收到了数据,就将数据让对应的代理去解析,如此一来完成了功能。
XMPPStream是一个单例对象,那么代理必然是一个集合,用来引用各个功能模块的代理对象。在XMPPFrameWork中定义了一个GCDMuticastDelegate类,用来存储和管理众多代理,数据来的时候,XMPPStream就可将数据作用于各个代理对象。( 这个类方便扩展,可以借鉴)
XMPPStream的代理设计目的也是出自为众多扩展服务,各个代理对象才是真正的功能受众对象。这样,如果需要添加功能类型扩展,只需让该扩展成为XMPPStream对象的代理,然后在代理方法中处理自己的逻辑便可。
五、随想
既然代理是一个对象,只要具备基本的应用条件便可做代理。在iOS 开发中,类本身是一个对象,那么它和它的实例对象都可以作为它自己的代理。这样就可以把一些通用的东西写在自己类中。
- 那么为了减轻View Controller的负荷,我们可以把一些View的代理定义成一个对象,然后让View Controller引用它即可。比如UITableView,我们可以定义个类实现UITableViewDataSource,则可以将UITableViewDataSource的方法抽离出来,让它成为我们自定义对象的实例方法,只提供Cell的定义接口。
- 将代理模式实现为Block形式,让自己成为自己的代理,让后在提供Block接口给代理对象。比如,将UIAlertView的代理改成block形式,那么创建的时候就直接在后面加上block处理,而且一个界面如果有多个UIAlertView的时候,可以减少tag判断。
- 可以用来自己做一些资源清理 (比如语音,视频资源)
- 通常在应用与用户交互时,改变一个成员变量的值会触发很多动作。借鉴GCDMuticastDelegate类,让这些动作代理自己管理,避免View Controller代码逻辑混乱。