在每一个应用程序中都是包含着许多松散耦合的对象,它们之间采用何种通信模式进行交互?哪一种通信机制是最佳的实践?这些都是值得思考的问题(诚然其中没有明确的答案,但是也有一些约定俗成的用法习惯)。
通常情况下,我们可以采用的通信模式包括:KVO,Notifications,delegation,blocks,target-action。
一、了解Communication Patterns
1、KVO
KVO机制用于通知对象属性的变化。
消息接收者-->接收对象属性变化的消息;消息发送者-->对象属性发生变化。
消息接收者通过注册对消息发送者的监听,还必须知道消息发送者的生命期,用以解除对其的注册。
2、Notifications
Notifications(消息通知)用于在代码间广播消息。
Notifications发送的消息是任意的,你可以通过 userInfo 字典或者用 NSNotification 消息载体装载发送的消息。
通过Notifications,消息发送者和接收者可以互不认识,即在松耦合的代码间传递消息。此外,这是一种单向的通信方式,即接收方不能回复Notifications的消息通知。
3、Delegation(委托)
委托的作用:传值;传事件。
在委托协议中定义任意需要的委托方法处理两个特定对象之间的通信。发送者即委托者,在委托协议中定义委托方法,接收者即被委托者,实现委托协议中的委托方法。
4、Blocks
Blocks通常用于回调事件处理等等,是一种相对较新的技术。一般情况下,block可以满足用delegation实现的消息传递机制,不过这两种机制还是有各自的特色。
潜在的风险:retain cycle。由于被blocks引用的变量都会被自动 retain 一次,如果不能保证被引用的变量置nil,那么使用Blocks就会出现 retain cycles。
例如:
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
//...complete actions
[manager otherAction];
[manager release];
};在这个例子中,由于manager和block相互持有,那么即使调用了release,还是形成了retain cycle。
为了解除retain cycle,那么代码修改为:
DoSomethingManager *manager = [[DoSomethingManager alloc] init];
manager.complete = ^{
//...complete actions
[manager otherAction];
manager.complete = nil;
[manager release];
};
由于 manager.complete = nil 这样就破坏了retain cycle。
再看一个NSOperation的例子:
self.queue = [[NSOperationQueue alloc] init];
MyOperation *operation = [[MyOperation alloc] init];
operation.completionBlock = ^{
[self finishedOperation];
};
[self.queue addOperation:operation];
乍一看,似乎存在一个retain cycle:
self --retain-->
queue --retain-->
operation --retain-->
blcok --retain-->
self
但是,一个operation添加到queue后,必定会在某一个时刻执行结束,然后退出queue,那么一旦operation退出queue后,retain cycle也就被打破了。
5、Target-Action
Target-Action是用来响应UI界面事件发送消息的典型模式。
及时消息响应的target为nil空,action也会沿着 responder chain(响应链)找到一个对象进行响应。
Target-Action的限制是消息发送不能携带任何自定义的负载消息。
二、Making the Right Choice
基于上述通信模式的特点,构建了如下的流程图,有助于在选择哪种模式做出正确的决定。(这张图的建议并不一定是最终的答案)

三、Framework Examples
1、KVO
NSOperationQueue 使用 KVO 机制观察 operation 的状态属性变化(isFinished,isExecuting,isCancelled),当operation的状态属性变化时,NSOperationQueue 就会接收到一个 KVO 消息通知。
NSOperationQueue -- 消息的接收者
operation -- 消息的发送者
显然,二者需要一个单向的通信机制,而且NSOperationQueue 只对 operation对象的状态属性的变化感兴趣,所以采用KVO是最佳的实践。

诚然,KVO是非常不错的实践方式。当然似乎也可以采用Delegation实现。
operation queue 是 operation 的委托(delegation),operation 可以调用诸如 operationDidFinish 或者 operationDidBeginExecuting 等委托协议方法通知 operation queue 有关 operation 状态属性的变化。
2、Notifications
Core Data 使用 notification 在事件间通信,例如:managed object context(NSManagedObjectContextDidChangeNotification)
managed object context 发送的变化通知,其消息接收方可以不必知晓谁是发送者,而且这不是一个UI事件,那么其接收者可以多个。说明这里需要一个单向的通信管道,那么 Notifications 就是唯一的选择了。

3、Delegation
Table View 的实现就采用了 Delegation 。例如tableView:didSelectRowAtIndexPath: 方法。为什么要以delegate调用的方式来实现?而又为啥不用target-action方式?
正如我们在流程图中看到的一样,使用target-action时,不能传递自定义的数据。而在选中table view的某个cell时,collection view不仅仅需要告诉我们有一个cell被选中了,还需要告诉我们是哪个cell被选中了(index path)。按照这样的一种思路,那么从流程图中可以看到应该使用delegation机制。

4、Blocks
关于block的介绍,我们来看看[NSURLSession dataTaskWithURL:completionHandler:]吧。从URL loading system返回到调用者,这个过程具体是如何传递消息的呢?首先,作为这个API的调用者,我们知道消息的发送者,但是我们并没有