我什么时候需要使用协议/代理?

时间:2014-11-26 19:46:42

标签: objective-c delegates protocols

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

但是现在我已经尝试将信息从父视图控制器发送到子视图控制器,并发现我需要协议/委托以便由于某种原因发回信息,尽管没有涉及segue,它全部在相同的“页面。”

我在这里缺少一些原则或规则吗?

1 个答案:

答案 0 :(得分: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类实际上并不需要知道哪种对象正在接收回调,它只需要知道对象(我们将调用该委托)响应选择器{{ 1}}。因此,我们定义了一个需要方法downloadFinished的协议。

downloadFinished:

然后我们可以说我们的ViewController符合这个协议:

@protocol DownloaderDelegate

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

@end

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

// ViewController.h

@interface ViewController: UIViewController <DownloaderDelegate>

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

// Delegate.h

/* ... */

@property(weak) id<DownloaderDelegate> delegate;

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