委托(Delegation)
委托是一种设计模式。这种模式虽然简单但是功能强大。委托就是程序中的一个对象代替别的对象来完成某些任务,或者是和别的对象一起协调来完成某些工作。其中,委托者持有对被委托者的引用,并在适当的时间发送消息给被委托者。通过这个消息委托者通知被委托者自己将要处理或者是已经处理了某种事件。被委托者可以相应该消息,更新自己的或者是程序中的用户界面或者是其他对象的状态;并且还可以通过返回值来告知委托者自己对该事件的处理情况,以便委托者决策应该如何采取进一步的行为。
委托和Cocoa框架(Delegation and Cocoa Framework)
在Cocoa框架中,委托者通常都是一个框架类的对象,而被委托者通常都是一个自定义的控制器对象。在需要进行内存管理的环境下,委托者都会持有对被委托者的一个弱引用;而在垃圾收集环境下,委托者持有对被委托者的强引用。在基础库中,UIKit,APPKit以及其他的Cocoa库和Cocoa框架中,存在有大量委托的例子。
一个委托的例子就是AppKit框架中的NSWindow类的对象作为委托者。NSWindow中声明了一个协议(Protocol),其中有一个方法windowShouldClose:。当用户点击窗体上的关闭按钮时,窗体对象就会发送windowShouldClose:消息给其委托(被委托者),询问是否确认关闭该窗体。窗体的委托可以通过返回布尔类型的值来控制窗体对象的行为。
如下图:
窗体对象(委托者)委托(被委托者)
委托与通知(Delegation and Notifications)
Cocoa框架中大多数类的委托都是自动地被注册为委托者发送的通知的观察者。委托只需要实现在框架类中已经声明了得通知方法来接收指定的通知消息即可。正如前面的示例一样,窗体对象会发送NSWindowsWillCloseNotification消息给所有观察者,但是只会给其委托发送一个windowShouldClose:消息。
数据源几乎和委托时等同的。区别在于他们和委托者的关系不同。数据源被委托是用来控制数据的,而不是处理用户界面的。委托者,通常都是一个视图对象,比如table 视图,会持有其数据源的引用并根据需要向其请求需要显示的数据。数据源,和委托类似,必需遵守一定的协议并实现协议中要求的最小的方法集。数据源要为其委托者视图的数据模型对象的内存管理负责。
通知是一种发送给一个或者多个观察者,用来通知其在程序中发生了某个事件的消息。Cocoa中的通知机制遵循的是一种广播的模式。它是一种程序中事件的发起者或者是处理者和其他想要知道该事件的对象沟通的一种方式。消息的接收者,也就是观察者响应该事件来变换自己的UI,行为或者是状态。发送通知的对象没有必要知道这些观察者都是谁。因此,通知时一种在程序中可以获得高效协作同时保持较高内聚性的机制。他减少了程序中对象相互之间的强依耐性(这种依耐性会大大降低程序中代码的可复用性)。基础库,AppKit以及其他的一些Objective-C框架中的很多类都定义了通知以便我们可以注册成为通知的观察者。
通知机制的核心就是一个进程中单一实例的对象,被叫做通知中心(NSNotificationCenter)。当一个对象发布一个通知时,通知会先被发布到通知中心。通知中心的作用相当于是交流所,作为通知的广播中心。程序中其他需要感知该事件的对象通过向通知中心注册就可以达到在事件发生时被通知中心及时通知到得目的。通知中心是可以以同步的方式向其观察者发送通知,也是可以通过使用通知队列(NSNotificationQueue)来异步地发送通知。
一个通知是用NSNotification类的一个对象来表示的。表示通知的对象中含有用于表示该通知的信息的字段:通知的名称,发布通知的对象以及一个用于表示其他补充信息的字典。这个字典被称为是userInfo字典。当通知被发送给对其感兴趣的观察者时,该表示通知的对象会被作为参数传入到处理该通知的方法中。
实现观察某个通知的时候,我们先要获取NSNotificationCenter的单实例对象并向其发送addObserver:selector:name:object:消息。通常情况下,这种注册的行为在应用程序启动后就要进行。addObserver:selector:name:object:方法的第二个参数是一个选择器,该选择器指定的是处理指定通知的具体方法。方法的原型必需如下:
-(void)myNotificationHandler:(NSNotification*)notif;
在该方法中,我们可以提取通知中的相关信息来帮助我们处理数据,特别是userInfo中的数据(如果userInfo存在的话)。
通常在发布通知之前,我们都需要定义一个全局的字符串常量来作为通知的名称。传统的做法是采用和应用程序相关的两个或者三个字母作为通知名称的前缀,例如:
NSString & AMMyNotification = @”AMMyNotification”;
通过向NSNotificationCenter的单实例对象发送postNotificationName:object:userInfo:(或者类似的)消息来完成通知的发布。在向通知中心发送通知之前,该方法会先创建一个表示该通知的对象。