托付和数据源
托付是 Apple 的框架里面使用广泛的模式,同一时候它是一个重要的 四人帮的书“设计模式”中的模式。
托付模式是单向的,消息的发送方(托付方)须要知道接收方(托付)。反过来就不是了。对象之间没有多少耦合,由于发送方仅仅要知道它的托付实现了相应的 protocol。
本质上。托付模式仅仅须要托付提供一些回调方法。就是说托付实现了一系列空返回值的方法。
不幸的是 Apple 的 API 并没有尊重这个原则,开发人员也效仿 Apple 进入了歧途。一个典型的样例是UITableViewDelegate 协议。
一些有 void 返回类型的方法就像回调
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
可是其它的不是
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
当托付者询问托付对象一些信息的时候。这就暗示着信息是从托付对象流向托付者,而不会反过来。 这个概念就和托付模式有些不同,它是一个另外的模式:数据源。
可能有人会说 Apple 有一个 UITableViewDataSouce protocol 来做这个(尽管使用托付模式的名字),可是实际上它的方法是用来提供真实的数据应该怎样被展示的信息的。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
此外,以上两个方法 Apple 混合了展示层和数据层。这显的非常糟糕。可是非常少的开发人员感到糟糕。并且我们在这里把空返回值和非空返回值的方法都天真地叫做托付方法。
为了分离概念,我们应该这样做:
- 托付模式:事件发生的时候。托付者须要通知托付
- 数据源模式: 托付方须要从数据源对象拉取数据
这个是实际的样例:
@class ZOCSignUpViewController;
@protocol ZOCSignUpViewControllerDelegate <NSObject>
- (void)signUpViewControllerDidPressSignUpButton:(ZOCSignUpViewController *)controller;
@end
@protocol ZOCSignUpViewControllerDataSource <NSObject>
- (ZOCUserCredentials *)credentialsForSignUpViewController:(ZOCSignUpViewController *)controller;
@end
@protocol ZOCSignUpViewControllerDataSource <NSObject>
@interface ZOCSignUpViewController : UIViewController
@property (nonatomic, weak) id<ZOCSignUpViewControllerDelegate> delegate;
@property (nonatomic, weak) id<ZOCSignUpViewControllerDataSource> dataSource;
@end
在上面的样例里面,托付方法须要总是有一个调用方作为第一个參数。否则托付对象可能被不能差别不同的托付者的实例。
此外,假设调用者没有被传递到托付对象,那么就没有办法让一个托付对象处理两个不同的托付者了。所以,以下这个方案就是人神共愤的:
- (void)calculatorDidCalculateValue:(CGFloat)value;
默认情况下,托付对象须要实现 protocol 的方法。
能够用@required
和 @optional
keyword来标记方法是否是必要的还是可选的。
@protocol ZOCSignUpViewControllerDelegate <NSObject>
@required
- (void)signUpViewController:(ZOCSignUpViewController *)controller didProvideSignUpInfo:(NSDictionary *);
@optional
- (void)signUpViewControllerDidPressSignUpButton:(ZOCSignUpViewController *)controller;
@end
对于可选的方法。托付者必须在发送消息前检查托付是否确实实现了特定的方法(否则会Crash):
if ([self.delegate respondsToSelector:@selector(signUpViewControllerDidPressSignUpButton:)]) {
[self.delegate signUpViewControllerDidPressSignUpButton:self];
}