iOS中的通信模式解析

每一个应用程序都或多或少的有几个松偶合的对象组成,而这些对象需要彼此之间通信来完成工作。这篇文章我们将学习所有的可变方案,用例子来探究如何在苹果的框架中使用这些方案,并且能够了解在什么时候使用哪种机制来完成最巧当的通信。
通过关于Foundation framework的问题,我们将透彻的知道Foundation中KVO, Notifications ,delegation, blocks 和 target-action中的通信机制。
当然,许多例子中没有明确的答案说应该用哪种机制。但是还是有在很多例子能够清晰的决定使用哪一种。
在这篇文章中,我们将使用“接收者”和“发送者”的关系。我们想说的是通过一些例子:比如tableview是发送者,它的delegate是接收者, 来说明这些通信方式。或者说core data管理的对象是通知的发送者,而任何使用者将是接收者。又或者slider是行为消息的发送者,而实现这个行为的响应者是接收者。或者有一个kvo编码特性的对象的改变是发送者,而观察者是接收者。了解了吗?
模式
首先我们将看下每种可用的通信模式的特殊的特性。基于这个,我们将构建一个表来帮助选择正确的通信模式。最终,我们将通过苹果的框架中一些例子来解释为什么我们在某些使用案例中要使用特殊的通信模式
KVO
KVO是一种通知对象的属性改变的机制。它是在foundation和许多建立在foundation上的frameworks种使用。为了更多的了解如何使用KVO,请读Daniel’s KVO AND KVC article。
如果你仅仅只对另一个对象的属性改变有兴趣的话,你可以使用KVO通信。其中有一些要求需要实现。第一,将收到改变的消息的接收对象必须知道发送对象的属性改变。此外,接收者必须知道发送者的生命周期,因为在发送者释放前,它必须取消注册观察。如果所有的这些都符合了,通信可以使用一对多,因为许多观察者能够注册对一个对象的跟新观察。
如果你计划使用KVO在Core Data对象上,你必须了解其中的一些差异。这跟Core Data的错误机制有关。一旦一个管理对象转变成一个错误,那么它也将通知观察者,尽管被观察者的属性没有改变。
Notifications
Notifications是一种非常好的工具在相对不相关的部分之间来广播消息,特别是如果这些消息更多的是更加提供消息性质的,并且它不需要期盼任何人来做一些事情。
Notifications能够发送任何消息,并且他们通过userInfo 或者 NSNotifications来使用。Notifications的特殊之处是接收者和发送者不需要知道彼此。他们可以在非常松耦合的模块间发送消息。因此,这种通信是单程的,你不能够回应一个消息。
Delegation
Delegation时在苹果的框架中广泛使用的模式。它允许我们定制一个对象的行为,来通知一些事件。对于代理事件的工作,消息发送者需要知道接收者(the delegate),但是接收者不需要。耦合是进一步松散的。因为发送者仅仅知道它的代理遵循了一些协议。
因为代理协议能够定义任何方法,你能够模型化你需要的通信。你能使用方法的形式,而且代理能够响应代理方法的返回值。代理是一种可变的和直观的通信模式,对于那些特殊的对象(在app架构中相对接近彼此)。
但是过度使用代理模式也会有危险。如果两个对象是紧耦合的,并且在没有对方的情况下不能工作,那么是不需要定义一个代理协议的。在这些例子中,这些对象能够了解对方的类型,并且直接与对方交流。比如说,UICollectionViewLayout 和 NSURLSessionConfiguration
blocks
blocks是在Objectvie-C中相对较新的,首先应用在OS X10.6和IOS4中。blocks经常能够实现delegation的功能。但是,2种模式各自有自己的需求和优点。
其中一种清晰的标准是,当有创建循环引用的风险时,不要使用blocks。如果发送者需要保留这个block的话,将不能保证引用会被置于nil,然后每个在block中对self的引用将会成为潜在的引用环。
假设我们想要实现一个tableview,但是我们想要使用block回调来代替delegate的selection方法,比如


self.myTableView.selectionHandler = ^void(NSIndexPath *selectedIndexPath) {
// handle selection ... };

这边的问题是self retain了tableview,而且tableview为了能够使用block不得不retain这个block。这个tableview不能将这个引用置为nil,因为它不知道什么时候不再需要这个block。如果我们不能保证引用环的解除,我们将retain这个发送者,这时blocks将不是一个好的选择。
NSOperation 是一个很好的例子来说明引用环不将是一个问题,因为在某一时刻它打破了引用环。


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

初看一下,感觉这是一个引用环:self retains 这个queue,queue retains 这个operation,这个operation retains 这个block,这个block retains self。但是增加operation到queue这个操作将导致operation在某一时刻被执行,并且在之后被移除出queue(如果它不能得到执行,我们将会有一个更大的问题)。一旦queue移除operation,保留环将被解除。
另外一个例子:我们将实现一个视频编码的类,我们调用encodeWithCompletionHandler:方法。为了使这个调用不会产生问题,我们必须确保编码对象在block中的引用在某一时刻被nil。内部代码如下:

@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; // <- Don't forget this!
}
@end

一旦我们的工作做完了,我们将会调用completion block,我们将它nil掉。如果我们发送的消息必须发送回一个一次性的回应,blocks将会是非常合适的。因为我们能够打破潜在的引用环。此外,它将使代码更加可读,因为把处理消息的代码放在了一起。因此blocks经常用在completion handers ,error handlers等等。
Target-Action
Target-Action是一种典型的模式被使用在发送消息来回应用户交涉时间。在IOS中的UIControl和在Mac中的NSControl/NSCell都支持这种模式。Target-Action在发送者和消息接收者之间建立了非常松的耦合。消息接收者不知道发送者,并且发送者也不是必须知道谁是接收者。如果target为nil,action将会使用responder chain直到它发现一个对象能够回应它。在iOS中,每一个control能够与多个Target-Action相联系。
基于target-action的通信的限制是发送的消息不能携带任何的通常的负载。在Mac上,action方法总是收到发送者作为第一参数。在iOS中,他们可以选择收到发送者,触发action的事件作为参数。但是,control发送action消息给其他对象是不可能的。
做正确的选择
基于以上不同模式的特征,我们建立了一个流程图来帮助采用正确的模式。但是,推荐的模式不一定是最终的选择,可能有类似的模式工作的同样好。但是在绝大多数例子中,它将指导你选择正确的模式。
iOS中的通信模式解析_第1张图片
原文链接:http://www.objc.io/issues/7-foundation/communication-patterns/

你可能感兴趣的:(iOS,ios,通信)