托付和數據源
托付是 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]; }
托付和數據源