我在iOS平台上工作,我想知道什么是委托函数和什么是回调函数?这两种函数之间有什么区别或者它们是相同的?委托功能的委托函数与回调函数
例子是UITableViewDelegate
协议numberOfRowsInSection
和示例回调函数是在appDelegate.m
我们可以创建在Objective-C自己的回调函数didReceiveLocalNotification
,如果是,举个例子...
谢谢。
我在iOS平台上工作,我想知道什么是委托函数和什么是回调函数?这两种函数之间有什么区别或者它们是相同的?委托功能的委托函数与回调函数
例子是UITableViewDelegate
协议numberOfRowsInSection
和示例回调函数是在appDelegate.m
我们可以创建在Objective-C自己的回调函数didReceiveLocalNotification
,如果是,举个例子...
谢谢。
一对夫妇的想法:
您建议didReceiveLocationNotification
是一个“回调函数”,但它实际上只是UIApplicationDelegate
协议的委托方法。所以,numberOfRowsInSection
和didReceiveLocalNotification
都只是委托方法。
当调度NSTimer
或定义UIGestureRecognizer
的处理程序时,更类似于通用回调函数的东西是selector
,其中方法名称的选择未预先确定。
或回调广泛用于CFArray
。
但是,你的问题的根源不是关于术语,而是一个如何定义接口的问题,调用者可以指定某个其他对象将在某个未来日期调用(异步)的方法。有几个共同的模式:
Block parameter to method:已经变得越来越普遍定义拿块作为参数的方法。例如,你可以有一个定义如下的方法:
- (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;
}
Delegate-protocol pattern:对象之间通信的另一种常用技术是委托协议模式。首先,定义的协议(“回调”接口的性质):
@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
,并定义了协议我的ThOD:
- (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__);
Selector pattern:您在一些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较老的类中使用得相当多,我将其加入了一些历史参考。
Notifications:提供某种异步方法的结果的其他机制是发送本地通知。当(a)网络请求结果的潜在接收者在请求启动时未知时,这通常是最有用的;或者(b)可能有多个类想要被告知这个事件。因此,网络请求可以在完成时发布特定名称的通知,任何有兴趣了解此事件的对象都可以通过NSNotificationCenter
将自己添加为本地通知的观察者。
这不是一个“回调”本身,但它代表另一个模式,通知某个对象完成某个异步任务。
这些都是的 “回调” 模式的几个例子。显然,所提供的例子是任意的和微不足道的,但希望它能让你了解你的选择。现在最常用的两种技术是块和委托模式。当需要简单和优雅的界面时,块越来越受到青睐。但对于丰富而复杂的界面,代表非常普遍。
给出的答案很好地解释了...... – Mrunal 2013-03-24 17:00:49
@rob非常有帮助的帖子谢谢:) – 2014-05-22 06:57:29
@Rob使用委托模式或块的选择是什么?我不知道我们是否可以谈论“最好”的做法,但是你更喜欢' - (void)didFinishedDownload:(NSData *)数据文件名:(NSString *)filename'和' - (void)downloadAsynchronously :(NSURL *)url filename:(NSString *)文件名完成:(void(^)(NSData * results,NSString * filename))completion'? – 2015-02-19 09:45:22
看看http://stackoverflow.com/questions/12050981/when-we-use-delegate-and-call-back-in-ios – 2013-03-24 11:02:38