您的位置:首页 > 移动开发 > IOS开发

iOS中消息的传递机制

2013-12-16 09:12 260 查看
注1:本文由破船[博客]译自Communication Patterns

本文目录如下所示:
可用的机制
做出正确的选择
Framework示例

小结
每个应用程序或多或少,都由一些松耦合的对象构成,这些对象彼此之间要想很好的完成任务,就需要进行消息传递。本文将介绍所有可用的消息传递机制,并通过示例来介绍这些机制在苹果的Framework中如何使用,同时,还介绍了一些最佳实践建议,告诉你什么时机该选择使用什么机制。

虽然这一期的主题是关于Foundation Framework的,不过本文中还介绍了一些超出Foundation Framework(KVO和Notification)范围的一些消息传递机制,另外还介绍了delegation,block和target-action。

大多数情况下,消息传递该使用什么机制,是很明确的了,当然了,在某些情况下该使用什么机制并没有明确的答案,需要你亲自去尝试一下。

本文中,会经常提及接收者[recipient]和发送者[sender]。在消息传递机制中具体是什么意思,我们可以通过一个示例来解释:一个table view是发送者,而它的delegate就是接收者。Core Data managed object context是notification的发送者,而获取这些notification的主体则是接收者。一个滑块(slider)是action消息的发送者,而在代码里面对应着实现这个action的responder就是接收者。对象中的某个属性支持KVO,那么谁修改这个值,谁就是发送者,对应的观察者(observer)则是接收者。

可用的机制
首先我们来看看每种机制的具体特点。在下一节中,我会结合一个流程图来介绍如何在具体情况下,选择正确的消息传递机制。最后,将介绍一些来自苹果Framework中的示例,并会解释在某种确定情况下为什么要选择固定的机制。

KVO
KVO提供了这样一种机制:当对象中的某个属性值发生了改变,可以对这些值的观察者做出通知。KVO的实现包含在Foundation里面,基于Foundation构建的许多Framework对KVO都有所依赖。要想了解更多关于如何使用KVO,可以阅读本期由Daniel写的的KVO和KVC文章。

如果对某个对象中值的改变情况感兴趣,那么可以使用KVO消息传递机制。这里有两个要求,首先,接收者(会接收到值发生改变的消息)必须知道发送者(值将发生改变的那个对象)。另外,接收者同样还需要知道发送者的生命周期,因为在销毁发送者对象之前,需要取消观察者的注册。如果这两个要求都满足了,消息传递过程中可以是1对多(多个观察者可以注册某个对象中的值)。

如果计划在Core Data对象上使用KVO,需要知道这跟一般的KVO使用方法有点不同。那就是必须结合Core Data的故障机制(faulting mechanism),一旦core data出现了故障,它将会触发其属性对应的观察者(即使这些属性值没有发生改变)。

Notification
在不相关的两部分代码中要想进行消息传递,通知(notifacation)是非常好的一种机制,它可以对消息进行广播。特别是想要传递丰富的信息,并且不一定指望有谁对此消息关心。

通知可以用来发送任意的消息,甚至包含一个userInfo字典,或者是NSNotifacation的一个子类。通知的独特之处就在于发送者和接收者双方并不需要相互知道。这样就可以在非常松耦合的模块间进行消息的传递。记住,这种消息传递机制是单向的,作为接收者是不可以回复消息的。

delegation
在苹果的Framework中,delegation模式被广泛的只用着。delegation允许我们定制某个对象的行为,并且可以收到某些确定的事件。为了使用delegation模式,消息的发送者需要知道消息的接收者(delegate),反过来就不用了。这里的发送者和接收者是比较松耦合的,因为发送者只知道它的delegate是遵循某个特定的协议。

delegate协议可以定义任意的方法,因此你可以准确的定义出你所需要的类型。你可以用函数参数的形式来处理消息内容,delegate还可以通过返回值的形式给发送者做出回应。如果只需要在相对接近的两个模块之间进行消息传递,那么Delegation是一种非常灵活和直接方式。

不过,过渡使用delegation也有一定的风险,如果两个对象的耦合程度比较紧密,相互之间不能独立存在,那么此时就没有必要使用delegate协议了,针对这种情况,对象之间可以知道相互间的类型,进而直接进行消息传递。例如UICollectionViewLayout和NSURLSessionConfiguration。

block
Block相对来说,是一种比较新的技术,它首次出现是在OS X 10.6和iOS 4中。一般情况下,block可以满足用delegation实现的消息传递机制。不过这两种机制都有各自的需求和优势。
当不考虑使用block时,一般主要是考虑到block极易引起retain环。如果发送者需要reatain block,而又不能确保这个引用什么时候被nil,这样就会发生潜在的retain环。
假设我们想要实现一个table view,使用block替代delegate,来当做selection的回调,如下:

self.myTableView.selectionHandler = ^void(NSIndexPath *selectedIndexPath) { // handle selection ... };
上面代码的问题在于self retain了table view,而table view为了之后能够使用block,进而 retain了block。而table view又不能把这个引用nil掉,因为它不知道什么时候不在需要这个block了。如果我们保证不了可以打破这个retain环,而我们又需要retain发送者,此时block不是好的选择。

NSOperation就可以很好的使用block,因为它能再某个时机打破retain环:

self.queue = [[NSOperationQueue alloc] init]; MyOperation *operation = [[MyOperation alloc] init]; operation.completionBlock = ^{ [self finishedOperation]; }; [self.queue addOperation:operation];

乍一看这似乎是一个retain环:self retain了queue,queue retain了operation,而operation retain了completion block,而completion blockretain了self。不过,在这里,将operation添加到queue时,会使operation在某个时机被执行,然后从queue中remove掉(如果没有被执行,就会有大问题了)。一单queue移除了operation之后,retain环就被打破了。

再来一个示例:这里实现了一个视频编码器的类,里面有一个名为encodeWithCompletionHandler:的方法。为了避免出现retain环,我们需要确保编码器这个对象能够在某个时机nil掉其对block的引用。其内部代码如下所示:

@interface Encoder () @property (nonatomic, copy) void (^completionHandler)(); @end @implementation Encoder - (void)encodeWithCompletionHandler:(void (^)())handler { self.completionHandler = handler; // do the asynchronous processing... } // This one will be called once the job is done - (void)finishedEncoding { self.completionHandler(); self.completionHandler = nil; //
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: