【问题标题】:When do I need to use protocols/delegates?我什么时候需要使用协议/委托?
【发布时间】:2014-11-27 04:36:22
【问题描述】:

起初我以为我只需要在从一个 ViewController 以 segue 序列向前和向后发送信息到另一个 ViewController 时实现这种模式。

但是现在我尝试将信息从父视图控制器发送到子视图控制器,并发现出于某种原因我需要协议/委托才能将信息发回,尽管不涉及任何转义,但一切都在相同的“页面”。

我在这里遗漏了一些原则或规则吗?

【问题讨论】:

  • 将委托视为一种设计模式,就像 MVC 一样,因此是通用的,可以在任何地方使用。

标签: objective-c delegates protocols


【解决方案1】:

协议/委托是在任何两个对象之间传递信息的一种方式。举个例子,一个从互联网下载大文件的类和一个视图控制器,然后将下载的信息呈现给用户。按照 MVC 模式,我们不想在下载类中处理下载信息的显示,所以我们需要以某种方式传递信息。

// Downloader.m

- (NSData *)download {
    /* do work */
    return downloadedInformation;
}

然后,在我们的视图控制器中:

// ViewController.m

#import "Downloader.h"

/* ... */

- (void)viewWillAppear {
    self.data = [[Downloader sharedInstance] download];
}

这会遇到一些问题。我们将挂起主队列(冻结的 UI),因为下载在主线程上运行并且需要一些时间,所以我们需要在后台执行它。但是如果我们在后台执行下载,那么获取下载信息并不像简单地从函数中返回一个值那么容易。

// Downloader.m

- (NSData *)download {
    NSData *downloadedInformation;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /* do work asynchronously */
    }

    return downloadedInformation; // will return before download is done
}

因此,我们需要一种方法在这些信息可用时跨类传递这些信息,但我们不知道它何时可用。一种方法是委托回调。假设我们的 viewController 有一个公共方法 downloadFinished:,它接受一个参数 data

// Downloader.m

#import ViewController.h

/* ... */

- (void)download:(ViewController *)viewController {
    NSData *downloadedInformation;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /* run download asynchronously, which will take a while */

        // assign downloadedInformation once download is completed,
        // guaranteeing that it is valid data

        // callback on main queue
        dispatch_async(dispatch_get_main_queue(), ^{
            [viewController downloadFinished:downloadedInformation];
        }
    }
}

现在我们将其作为参数传递给ViewController 中的方法,而不是返回一个值。然后,在ViewController

- (void)downloadFinished:(NSData *)data {
    [self updateUIWithData:data]; // update UI
}

那么协议和委托从何而来?假设您想在不同的类中调用downloadFinished:,该类可能是也可能不是视图控制器。 Downloader 类实际上并不需要知道什么样的对象正在接收回调,它只需要知道对象(我们将称之为委托)响应选择器downloadFinished。所以我们定义了一个需要downloadFinished:方法的协议。

@protocol DownloaderDelegate

    - (void)downloadFinished:(NSData *)data;

@end

那么我们可以说我们的 ViewController 遵守了这个协议:

// ViewController.h

@interface ViewController: UIViewController <DownloaderDelegate>

在我们的 Downloader 类上定义一个委托:

// Delegate.h

/* ... */

@property(weak) id<DownloaderDelegate> delegate;

并重写我们的函数以接收任何符合我们委托的对象,而不仅仅是 ViewController 的实例。

- (void)download {
    NSData *downloadedInformation;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /* run download asynchronously, which will take a while */

        // assign downloadedInformation once download is completed,
        // guaranteeing that it is valid data

        // callback on main queue
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.delegate downloadFinished:downloadedInformation];
        }
    }
}

现在我们使用委托模式在类之间传递信息。如果您仍然好奇,Apple 的文档可以阅读更多内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-17
    • 2016-04-19
    • 1970-01-01
    相关资源
    最近更新 更多