At the moment I am teaching myself iOS programming, and one concept I find really hard to wrap my head around is delegation. What is it? Why and how is it used? What is the advantage? The technical writing from the book I'm reading makes it hard to understand.
2 Answers
To understand delegates
, you have to understand protocols
.
A protocol
is like a service contract. When an object (most often a UIViewController
subclass, but not always) signs that contract, it is saying "I am interested in providing logic to back the message you send me". This is similar to NSNotificationCenter
in regards to signing up for a level of interest, the difference being an object that employs delegation can only have one delegate
at a time, where as multiple objects can sign up for the same NSNotification
.
Apple uses delegation pervasively. More and more, though, you're seeing Apple move a lot of their API over to blocks
, which are similar to callbacks
in other languages.
That being said, delegation helps maintain MVC, even though I would argue delegation is a design pattern in and of itself. It helps to separate models from controllers. Such as in John Cartwright's example, a UITableView
knows how to display rows and sections. It knows how to reuse UITableViewCells
for performance reasons. It knows all of the other stuff a UIScrollView
knows. But it doesn't know which cells to display. It doesn't know what to populate those cells with. It doesn't know which cells to reuse for a given NSIndexPath
. This really should be the job of the controller, anyways. Delegation allows the table view to offload this non-view logic onto an object that should have that responsibility anyways.
More than that, you're not locked into one delegate for the entire lifetime of an object. You could very easily have multiple datasources for a given UITableView
and switch them around at run time as needed.
So, on the one hand, delegation is great for supplying data to and responding to interactions from an object. You'll see it in a lot of UIKit classes, such a UITableView
, UIPickerView
, UICollectionView
, etc.
But delegation is also very useful when you want to pass information between objects. You can very easily create your own protocols and sign up your own objects to follow them. Furthermore, protocol methods are @required
by default, but you can specify some methods to be @optional
. This can give you some nice flexibility if you need it. Let's say you have a parent view controller and a child view controller. Maybe you're using the new Containment API to do this. Typically, if you need to pass information from the parent to the child, you do this with a property. Done. But what if you need to pass information from the child back to the parent? Maybe something changes in the child and you need to notify the parent. Sure, you could do some KVO on certain values. But maybe you want to know when a button is pressed. Simply create a new protocol in the child view controller
@protocol MyChildDelegate
- (void)buttonWasTappedInChild:(MyChildViewController *)childViewController;
@end
@interface MyChildViewController : UIViewController
@property (weak, nonatomic) id <MyChildDelegate> delegate;
@end
In MyChildViewController, when your button is tapped, simply check if your delegate responds to the delegate message (if it's required and your delegate doesn't implement the method, you'll crash. You can make the method @optional
if you need to) and send it:
- (IBAction)someButtonTapped:(id)sender {
if ([self.delegate respondsToSelector:@selector(buttonWasTappedInChild:)]) {
[self.delegate buttonWasTappedInChild:self];
}
}
Then set the delegate of your MyChildViewController to self
and implement - (void)buttonWasTappedInChild:(MyChildViewController *)childViewController
in your parent view controller. BOOM! You have information passed from a child up to the parent. The relationship between the two objects doesn't even need to be as close as parent/child. It's a service contract, so as long as the object signing up holds up its end of the bargain by implementing the required methods, you're golden!
NOTE: Delegates should be weak/assign properties, otherwise you'll enter a retain cycle where neither object can be deallocated.
Hope this helps!
Delegates are objects that implement certain functions when it doesn't make sense to implement those functions on the normal object. It is a form of dependency injection.
For a concrete example, look at the UITableViewDelegate protocol. These methods don't make sense for a table view to implement directly, because actions for selecting a table view row will be different in each app and maybe in each table view. The delegate has a method -tableView:didSelectRowAtIndexPath:
so you can create an object that handles row selection without subclassing the table view for every separate action you want to implement.