gpt4 book ai didi

ios - 委托(delegate)函数 vs 回调函数

转载 作者:技术小花猫 更新时间:2023-10-29 10:11:30 42 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





Problem understanding Delegating pattern & callback functions in Objective C

(3 个回答)


8年前关闭。




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

委托(delegate)函数的例子是 numberOfRowsInSectionUITableViewDelegate回调函数的协议(protocol)和例子是didReceiveLocalNotificationappDelegate.m
我们可以在 Objective-C 中创建我们自己的回调函数吗,如果是,举个例子......

谢谢..

最佳答案

一些想法:

  • 您建议 didReceiveLocationNotification 是一个“回调函数”,但它实际上只是 UIApplicationDelegate 的一个委托(delegate)方法协议(protocol)。所以,两个 numberOfRowsInSectiondidReceiveLocalNotification只是委托(delegate)方法。

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

    或者回调在 CFArray 中被广泛使用.
  • 但是,您的问题的根源不是术语,而是如何定义接口(interface)的问题,调用者可以在其中指定某个其他对象将在 future 某个日期(异步)调用的方法。有几种常见的模式:
  • 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 :对象之间通信的另一种常用技术是委托(delegate)协议(protocol)模式。首先,您定义协议(protocol)(“回调”接口(interface)的性质):
    @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 的原始 View Controller 类必须符合 DownloadDelegate协议(protocol):
    @interface ViewController () <DownloadDelegate>

    @end

    并定义协议(protocol)方法:
    - (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];
    }

    就我个人而言,我发现这种模式过于脆弱并且依赖于协调接口(interface)而没有编译器的任何帮助。但考虑到这种模式在 Cocoa 的旧类中使用得相当多,我将它包括在内作为一些历史引用。
  • Notifications :提供某些异步方法结果的另一种机制是发送本地通知。这通常在以下情况下最有用: (a) 在发起请求时网络请求结果的潜在接收者是未知的;或 (b) 可能有多个类(class)想要获知此事件。因此,网络请求可以在完成后发布特定名称的通知,并且任何有兴趣被通知此事件的对象都可以通过 NSNotificationCenter 将自己添加为该本地通知的观察者。 .

    这本身不是“回调”,但确实代表了另一种模式,用于通知对象某些异步任务的完成。

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

    关于ios - 委托(delegate)函数 vs 回调函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15597601/

    42 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com