2013-03-24 121 views
14

我在iOS平台上工作,我想知道什么是委托函数和什么是回调函数?这两种函数之间有什么区别或者它们是相同的?委托功能的委托函数与回调函数

例子是UITableViewDelegate协议numberOfRowsInSection和示例回调函数是在appDelegate.m

我们可以创建在Objective-C自己的回调函数didReceiveLocalNotification,如果是,举个例子...

谢谢。

+0

看看http://stackoverflow.com/questions/12050981/when-we-use-delegate-and-call-back-in-ios – 2013-03-24 11:02:38

回答

67

一对夫妇的想法:

  1. 您建议didReceiveLocationNotification是一个“回调函数”,但它实际上只是UIApplicationDelegate协议的委托方法。所以,numberOfRowsInSectiondidReceiveLocalNotification都只是委托方法。

    当调度NSTimer或定义UIGestureRecognizer的处理程序时,更类似于通用回调函数的东西是selector,其中方法名称的选择未预先确定。

    或回调广泛用于CFArray

  2. 但是,你的问题的根源不是关于术语,而是一个如何定义接口的问题,调用者可以指定某个其他对象将在某个未来日期调用(异步)的方法。有几个共同的模式:

    • 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对象看到一个模式(例如NSTimerUIPanGestureRecognizer)是将选择器作为参数传递的概念。例如,我们可以这样来定义我们的下载方法如下:

      - (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将自己添加为本地通知的观察者。

      这不是一个“回调”本身,但它代表另一个模式,通知某个对象完成某个异步任务。

这些都是的 “回调” 模式的几个例子。显然,所提供的例子是任意的和微不足道的,但希望它能让你了解你的选择。现在最常用的两种技术是块和委托模式。当需要简单和优雅的界面时,块越来越受到青睐。但对于丰富而复杂的界面,代表非常普遍。

+3

给出的答案很好地解释了...... – Mrunal 2013-03-24 17:00:49

+0

@rob非常有帮助的帖子谢谢:) – 2014-05-22 06:57:29

+0

@Rob使用委托模式或块的选择是什么?我不知道我们是否可以谈论“最好”的做法,但是你更喜欢' - (void)didFinishedDownload:(NSData *)数据文件名:(NSString *)filename'和' - (void)downloadAsynchronously :(NSURL *)url filename:(NSString *)文件名完成:(void(^)(NSData * results,NSString * filename))completion'? – 2015-02-19 09:45:22