我在 iOS 平台上工作,我想知道什么是委托函数,什么是回调函数?两种类型的功能有什么区别或者它们是相同的?
委托函数的示例numberOfRowsInSection
在UITableViewDelegate
协议中,回调函数的示例didReceiveLocalNotification
在appDelegate.m
我们可以在Objective-C中创建自己的回调函数吗,如果是,举个例子......
谢谢..
我在 iOS 平台上工作,我想知道什么是委托函数,什么是回调函数?两种类型的功能有什么区别或者它们是相同的?
委托函数的示例numberOfRowsInSection
在UITableViewDelegate
协议中,回调函数的示例didReceiveLocalNotification
在appDelegate.m
我们可以在Objective-C中创建自己的回调函数吗,如果是,举个例子......
谢谢..
几个想法:
您建议这didReceiveLocationNotification
是一个“回调函数”,但它实际上只是UIApplicationDelegate
协议的一个委托方法。因此,两者numberOfRowsInSection
和didReceiveLocalNotification
都是简单的委托方法。
更类似于通用回调函数的东西是selector
在调度 aNSTimer
或为 a 定义处理程序时UIGestureRecognizer
,其中方法名称的选择不是预先确定的。
或者回调在CFArray
.
但是,您问题的根源不是术语,而是如何定义接口的问题,调用者可以在其中指定某个其他对象将在将来某个日期(异步)调用的方法。有两种常见的模式:
方法的块参数:定义将块作为参数的方法越来越普遍。例如,您可以有一个定义如下的方法:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(void (^)(NSData *results, NSString *filename))completion {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(data, filename);
});
}];
[task resume];
return task;
}
第三个参数 ,completion
是下载完成后将调用的代码块。因此,您可以按如下方式调用该方法:
[self downloadAsynchronously:url filename:filename completion:^(NSData *results, NSString *filename) {
NSLog(@"Downloaded %d bytes", [results length]);
[results writeToFile:filename atomically:YES];
}];
NSLog(@"%s done", __FUNCTION__);
您会看到“完成”消息立即出现,completion
下载完成后将调用该块。诚然,要习惯构成块变量/参数定义的乱七八糟的标点符号需要一段时间,但是一旦您熟悉了块语法,您就会真正欣赏这种模式。它消除了调用某些方法和定义某些单独的回调函数之间的脱节。
如果您想简化将块作为参数处理的语法,您实际上可以typedef
为您的完成块定义一个:
typedef void (^DownloadCompletionBlock)(NSData *results, NSString *filename);
然后方法声明本身被简化:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(DownloadCompletionBlock)completion {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(data, filename);
});
}];
[task resume];
return task;
}
委托协议模式:对象之间通信的另一种常用技术是委托协议模式。首先,您定义协议(“回调”接口的性质):
@protocol DownloadDelegate <NSObject>
- (NSURLSessionTask *)didFinishedDownload:(NSData *)data filename:(NSString *)filename;
@end
然后,定义将调用此DownloadDelegate
方法的类:
@interface Downloader : NSObject
@property (nonatomic, weak) id<DownloadDelegate> delegate;
- (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate;
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename;
@end
@implementation Downloader
- (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate {
self = [super init];
if (self) {
_delegate = delegate;
}
return self;
}
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate didFinishedDownload:data filename:filename];
});
}];
[task resume];
return task;
}
@end
最后,使用这个新Downloader
类的原始视图控制器必须符合DownloadDelegate
协议:
@interface ViewController () <DownloadDelegate>
@end
并定义协议方法:
- (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename {
NSLog(@"Downloaded %d bytes", [data length]);
[data writeToFile:filename atomically:YES];
}
并执行下载:
Downloader *downloader = [[Downloader alloc] initWithDelegate:self];
[downloader downloadAsynchronously:url filename:filename];
NSLog(@"%s done", __FUNCTION__);
选择器模式:您在某些 Cocoa 对象(例如 )中看到的模式NSTimer
是UIPanGestureRecognizer
将选择器作为参数传递的概念。例如,我们可以如下定义下载器方法:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename target:(id)target selector:(SEL)selector {
id __weak weakTarget = target; // so that the dispatch_async won't retain the selector
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[weakTarget performSelector:selector
withObject:data
withObject:filename];
#pragma clang diagnostic pop
});
}];
[task resume];
return task;
}
然后,您将按如下方式调用它:
[self downloadAsynchronously:url
filename:filename
target:self
selector:@selector(didFinishedDownload:filename:)];
但是您还必须定义下载完成时将调用的单独方法:
- (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename {
NSLog(@"Downloaded %d bytes", [data length]);
[data writeToFile:filename atomically:YES];
}
就个人而言,我发现这种模式太脆弱了,并且在没有编译器帮助的情况下依赖于协调接口。但是我将它包含在一些历史参考中,因为这种模式在 Cocoa 的旧类中使用得相当多。
通知:提供某些异步方法结果的另一种机制是发送本地通知。这通常在以下情况下最有用: (a) 网络请求结果的潜在接收者在发起请求时未知;或 (b) 可能有多个班级希望获悉此事件。因此,网络请求可以在完成后发布特定名称的通知,并且任何有兴趣了解此事件的对象都可以通过NSNotificationCenter
.
这本身不是“回调” ,但确实代表了另一种模式,用于通知对象某些异步任务的完成。
这些是“回调”模式的一些示例。显然,提供的示例是任意且琐碎的,但希望它能让您了解您的替代方案。如今,两种最常见的技术是块模式和委托模式。当需要简单而优雅的界面时,块越来越受到青睐。但是对于丰富复杂的接口,委托是很常见的。