委托函数与回调函数 [英] delegate function vs callback function

查看:27
本文介绍了委托函数与回调函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在iOS平台工作,我想知道什么是委托函数,什么是回调函数?这两种功能有什么区别或者是一样的??

I work on iOS platform , I want to know what is a delegate function and what is a callback function ? what is the difference between the two types of function or they are the same ??

委托函数示例为UITableViewDelegate协议中的numberOfRowsInSection,回调函数示例为appDelegate.m中的didReceiveLocalNotification>

example of delegate function is numberOfRowsInSection in UITableViewDelegate protocol and example of callback function is didReceiveLocalNotification in appDelegate.m

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

Can we create our own callback function in Objective-C , if YES ,give an example ...

谢谢...

推荐答案

一些想法:

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

  1. You suggest that didReceiveLocationNotification was a "callback function", but it's actually just a delegate method of the UIApplicationDelegate protocol. So, both numberOfRowsInSection and didReceiveLocalNotification are simply delegate methods.

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

Something more akin to a generic callback function would be the selector when scheduling a NSTimer or defining the handler for a UIGestureRecognizer, where the choice of method name is not predetermined.

Or 回调在 CFArray.

Or callbacks are used extensively in CFArray.

但是,您的问题的根源不是术语,而是如何定义接口的问题,调用者可以在其中指定某个其他对象将在未来某个日期(异步)调用的方法.有几种常见的模式:

But, the root of your question is less on the terminology, but rather a question of how to define an interface where the caller can specify a method that some other object will invoke (asynchronously) at some future date. There are a couple of common patterns:

  • 方法的块参数:定义将块作为参数的方法越来越普遍.例如,您可以有一个如下定义的方法:

  • Block parameter to method: It is increasingly common to define methods that take a block as a parameter. For example, you can have a method that is defined as follows:

- (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 是一段代码,下载完成后将调用该代码块.因此,您可以按如下方式调用该方法:

That third parameter, completion, is a block of code that will be called with the download is done. Thus, you can invoke that method as follows:

[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 块.诚然,习惯构成块变量/参数定义的笨拙的标点符号需要一段时间,但是一旦您熟悉块语法,您就会真正欣赏这种模式.它消除了调用某些方法和定义某些单独的回调函数之间的脱节.

You'll see that "done" message appear immediately, and that completion block will be called when the download is done. It admittedly takes a while to get used to the ungainly mess of punctuation that constitutes a block variable/parameter definition, but once you're familiar with the block syntax, you'll really appreciate this pattern. It eliminates the disconnect between the invoking of some method and the defining of some separate callback function.

如果你想简化处理块作为参数的语法,你实际上可以为你的完成块定义一个typedef:

If you want to simplify the syntax of dealing with blocks as parameters, you can actually define a typedef for your completion block:

typedef void (^DownloadCompletionBlock)(NSData *results, NSString *filename);

然后方法声明本身就被简化了:

And then the method declaration, itself, is simplified:

- (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: The other common technique for communicating between objects is the delegate-protocol pattern. First, you define the protocol (the nature of the "callback" interface):

    @protocol DownloadDelegate <NSObject>
    
    - (NSURLSessionTask *)didFinishedDownload:(NSData *)data filename:(NSString *)filename;
    
    @end
    

    然后,您定义将调用此 DownloadDelegate 方法的类:

    Then, you define your class that will be invoking this DownloadDelegate method:

    @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 协议:

    And finally, the original view controller which uses this new Downloader class must conform to the DownloadDelegate protocol:

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

  • Selector pattern: A pattern that you see in some Cocoa objects (e.g. NSTimer, UIPanGestureRecognizer) is the notion of passing a selector as a parameter. For example, we could have defined our downloader method as follows:

    - (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;
    }
    

    然后您可以按如下方式调用它:

    You'd then invoke that as follows:

    [self downloadAsynchronously:url
                        filename:filename
                          target:self
                        selector:@selector(didFinishedDownload:filename:)];
    

    但是您还必须定义在下载完成时将调用的单独方法:

    But you also have to define that separate method that will be called when the download is done:

    - (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename {
        NSLog(@"Downloaded %d bytes", [data length]);
        [data writeToFile:filename atomically:YES];
    }
    

    就我个人而言,我发现这种模式过于脆弱并且依赖于协调接口而没有编译器的任何帮助.但考虑到这种模式在 Cocoa 的旧类中使用得相当多,我将它包含在内以作为一些历史参考.

    Personally, I find this pattern to be far too fragile and dependent upon coordinating interfaces without any assistance from the compiler. But I include it for a bit of historical reference given that this pattern is used quite a bit in Cocoa's older classes.

    通知:提供一些异步方法的结果是发送本地通知.这通常在以下情况下最有用: (a) 在发起请求时网络请求结果的潜在接收者是未知的;或 (b) 可能有多个班级想要获知此事件.因此,网络请求可以在完成后发布特定名称的通知,并且任何有兴趣了解此事件的对象都可以通过 NSNotificationCenter 将自己添加为该本地通知的观察者.

    Notifications: The other mechanism to provide the results of some asynchronous method is to send a local notification. This is generally most useful when either (a) the potential recipient of the results of the network request is unknown at the time the request was initiated; or (b) there may be multiple classes that want to be informed of this event. So the network request can post a notification of a particular name when it's done, and any object that is interested in being informed of this event can add themselves as an observer for that local notification through the NSNotificationCenter.

    这本身不是回调",但确实代表了另一种模式,让对象被告知某个异步任务的完成.

    This is not a "callback" per se, but does represent another pattern for an object to be informed of the completion of some asynchronous task.

    这些是回调"模式的几个例子.显然,提供的示例是随意且琐碎的,但希望它能让您了解您的替代方案.现在,两种最常见的技术是块和委托模式.当需要一个简单而优雅的界面时,块越来越受到青睐.但是对于丰富复杂的接口,委托是很常见的.

    Those are a few examples of "callback" patterns. Clearly, the example provided was arbitrary and trivial, but hopefully it should give you an idea of your alternatives. The two most common techniques, nowadays, are blocks and delegate patterns. Blocks are increasingly being preferred when a simple and elegant interface is needed. But for rich and complicated interfaces, delegates are very common.

    这篇关于委托函数与回调函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆