协议/委托是在任何两个对象之间传递信息的一种方式。举个例子,一个从互联网下载大文件的类和一个视图控制器,然后将下载的信息呈现给用户。按照 MVC 模式,我们不想在下载类中处理下载信息的显示,所以我们需要以某种方式传递信息。
// Downloader.m
- (NSData *)download {
/* do work */
return downloadedInformation;
}
然后,在我们的视图控制器中:
// ViewController.m
#import "Downloader.h"
/* ... */
- (void)viewWillAppear {
self.data = [[Downloader sharedInstance] download];
}
这会遇到一些问题。我们将挂起主队列(冻结的 UI),因为下载在主线程上运行并且需要一些时间,所以我们需要在后台执行它。但是如果我们在后台执行下载,那么获取下载信息并不像简单地从函数中返回一个值那么容易。
// Downloader.m
- (NSData *)download {
NSData *downloadedInformation;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* do work asynchronously */
}
return downloadedInformation; // will return before download is done
}
因此,我们需要一种方法在这些信息可用时跨类传递这些信息,但我们不知道它何时可用。一种方法是委托回调。假设我们的 viewController 有一个公共方法 downloadFinished:,它接受一个参数 data。
// Downloader.m
#import ViewController.h
/* ... */
- (void)download:(ViewController *)viewController {
NSData *downloadedInformation;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* run download asynchronously, which will take a while */
// assign downloadedInformation once download is completed,
// guaranteeing that it is valid data
// callback on main queue
dispatch_async(dispatch_get_main_queue(), ^{
[viewController downloadFinished:downloadedInformation];
}
}
}
现在我们将其作为参数传递给ViewController 中的方法,而不是返回一个值。然后,在ViewController:
- (void)downloadFinished:(NSData *)data {
[self updateUIWithData:data]; // update UI
}
那么协议和委托从何而来?假设您想在不同的类中调用downloadFinished:,该类可能是也可能不是视图控制器。 Downloader 类实际上并不需要知道什么样的对象正在接收回调,它只需要知道对象(我们将称之为委托)响应选择器downloadFinished。所以我们定义了一个需要downloadFinished:方法的协议。
@protocol DownloaderDelegate
- (void)downloadFinished:(NSData *)data;
@end
那么我们可以说我们的 ViewController 遵守了这个协议:
// ViewController.h
@interface ViewController: UIViewController <DownloaderDelegate>
在我们的 Downloader 类上定义一个委托:
// Delegate.h
/* ... */
@property(weak) id<DownloaderDelegate> delegate;
并重写我们的函数以接收任何符合我们委托的对象,而不仅仅是 ViewController 的实例。
- (void)download {
NSData *downloadedInformation;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* run download asynchronously, which will take a while */
// assign downloadedInformation once download is completed,
// guaranteeing that it is valid data
// callback on main queue
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate downloadFinished:downloadedInformation];
}
}
}
现在我们使用委托模式在类之间传递信息。如果您仍然好奇,Apple 的文档可以阅读更多内容。