Reputation: 37048
At first I thought I only needed to implement this pattern when sending information from one ViewController to another, forwards and backwards, in a segue sequence.
But now I've tried sending information from a parent view controller to a sub-view controller and found I needed protocol/delegate in order to send info back for some reason, despite that no segues are involved, it's all in the same "page."
Is there some principle or rule I'm missing here?
Upvotes: 0
Views: 48
Reputation: 1217
Protocols/delegates are a way of passing information between any two objects. Take for example a class that downloads large files from the internet and a view controller that then presents that downloaded information to the user. In compliance with the MVC pattern, we don't want to handle the displaying of the download information in the download class, so we need to pass the information on somehow.
// Downloader.m
- (NSData *)download {
/* do work */
return downloadedInformation;
}
Then, in our view controller:
// ViewController.m
#import "Downloader.h"
/* ... */
- (void)viewWillAppear {
self.data = [[Downloader sharedInstance] download];
}
This runs into a few problems. We'll hang the main queue (frozen UI) because the download is running on the main thread and takes some time, so we need to perform it in the background. But if we perform our download in the background, getting the download information is not as easy as simply returning a value from a function.
// 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
}
So we need a way to pass this information across classes once it's available, but we don't know when it's available. One way of doing this is a delegate callback. Suppose our viewController has a public method downloadFinished:
that takes in a parameter 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];
}
}
}
Now instead of returning a value we're passing it in as a parameter to a method in ViewController
. Then, in ViewController
:
- (void)downloadFinished:(NSData *)data {
[self updateUIWithData:data]; // update UI
}
So where do the protocols and delegates come in? Suppose you wanted to call downloadFinished:
in a different class, one that may or may not be a view controller. The Downloader
class doesn't actually need to know what kind of object is receiving the callback, it just needs to know that the object (which we'll call the delegate) responds to the selector downloadFinished
. So we define a protocol that requires the method downloadFinished:
.
@protocol DownloaderDelegate
- (void)downloadFinished:(NSData *)data;
@end
Then we can say that our ViewController adheres to this protocol:
// ViewController.h
@interface ViewController: UIViewController <DownloaderDelegate>
Define a delegate on our Downloader class:
// Delegate.h
/* ... */
@property(weak) id<DownloaderDelegate> delegate;
And rewrite our function to take in any object conforming to our delegate instead of just instances of 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];
}
}
}
Now we are using the delegate pattern to pass information between classes. Apple's docs have more reading if you're still curious.
Upvotes: 1