Sunday, January 24, 2010

Cocoa and Delegates

Delegates are probably one of the toughest concepts for iPhone/OSX developer newcomers to grasp. I'm going to try to keep it simple, and demonstrate how to build your own delegate protocol into your classes.

A delegate is merely a design pattern in Objective-C. Simply put, it provides a way for objects to "listen" for interesting things happening in your object and act on them without your object knowing anything about the object(s) listening. Delegates are used heavily in the Cocoa environment. Most often you will see delegates used to trigger methods on a UIViewController, although they can be implemented on anything.

Let's think for a moment what type of problem this solves. Let's say we have a UIViewController which has a UITableView in its view. When a table row is tapped, we want to slide a new view onto the screen. We don't want UITableView handling this task, so how do we accomplish this?

The UIViewController needs to handle the new view (thus it's name.) So the UITableView needs to somehow inform the UIViewController that a row was tapped so it can take action. Therefore, the UIViewController needs to be the delegate of the UITableView. So we need to setup two things: We need to inform the UITableView what it's delegate object is, and we need to inform the delegate object what delegate protocol(s) it conforms to.

We begin by setting the delegate property of the UITableView to the UIViewController object. If you are using Interface Builder, you can simply connect the delegate property of the UITableView to the UIViewController (typically the File's Owner.) Otherwise you might handle this in the viewDidLoad{} of the UIViewController, something like:

self.tableView.delegate = self;


You will also want to tell the view controller that it understands the UITableViewDelegate protocol, so you would put this in the UIViewController .h interface declaration:

@interface MyViewController : UIViewController
<UITableViewDelegate> {}
(Note: if your view controller is an extension of UITableViewController, you will not need to declare the UITableViewDelegate protocol.)

And you are set! Now when you implement a method of the UITableViewDelegate in the UIViewController, this method will trigger when something happens in the table view for that delegate method. So for instance, you put this in your UIViewController .m file:


- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// push the new view onto the stack here
}
When a row is selected in the table, this method will get triggered.

So now begs the question, how do you add your own delegate protocol to your custom classes? Here is an example. Let's say you want to be able to trigger methods of other classes when interesting things happen in your class. The first thing you need to do is define what delegate methods are available to the delegate object. You would first put something like this at the top of your class .h file, above the implementation declaration:


@protocol MyCustomClassDelegate <NSObject>;
- (void)didClickDone:(UIButton *)button;
@optional
-(void)didClickCancel:(UIButton *)button;
@end


Where MyCustomClassDelegate is the name of your class delegate protocol (your class name appended with "Delegate"), and didClickDone and didClickCancel are the names of your two delegate methods. You can add as many methods as you wish. You not need to implement these methods in your class, that is up to the delegate to do. Notice the line with @optional. This means that the following methods are optional for the delegate to implement.

Now we need to setup the delegate property:


@interface MyCustomClass : NSObject {
id <MyCustomClassDelegate> delegate;
}

@property (nonatomic, assign)
id <MyCustomClassDelegate> delegate;

And finally in the .m file, synthesize the delegate:


@synthesize delegate;


Note we do not need to release the delegate in the dealloc method, as this is not a retained object, it is just an assigned id.

Ok, for the final part of your class, you need to fire off the delegate method when something interesting happens. For instance, someone clicks the done button. In your IBAction for the done button, you do just that:


-(IBAction)done:(UIButton *)button {
[self.delegate didClickDone:button];
}


And so on, for each delegate method. Ok, that is it for the class. Now, for the delegate class. To be the delegate of MyCustomClass, you need to declare that you implement the MyCustomClassDelegate protocol (.h file):


@interface MyViewController:
UIViewController <MyCustomClassDelegate> { }


And then, we implement the delegate methods (.m file):


-(void)didClickDone:(UIButton *)button {
// do something here!
}


One last thing to note, a class can be the delegate for several objects, just comma-separate the delegates:


@interface MyViewController:
UIViewController <MyCustomClassDelegate,SomeOtherDelegate> { }



I hope that helps clear up delegates a bit! Please leave your comments below.

10 comments:

Tim said...

Thanks for this. I was having problems wrapping my mind around this and now I think I grok it.

Jason Buckner said...

Thanks for this post! Really helped clear up both sides of the delegation concept for me.

Yiyi said...

After reading two books "Beginning iPhone 3 Development" and "iPhone for Programmers: An App-Driven Approach", I am still confused about delegate and class.

Thanks for your tutorial, now I understand why we use delegate and how to use it.

Gabriel Ayuso said...

Nice explanation, just wanted to note the importance of using
assign in the @property declaration:

@property (nonatomic, assign)
id delegate;


Using assign will prevent having circular dependencies between the MyCustomClass and its delegate.

Also it's good to note that in the implementation of
-(IBAction)done:(UIButton *)button; one would most likely make sure that the delegate is valid and it responds to the didClickDone: selector. This will prevent errors.

iLiuqi said...

Aha, nice explanation!

Eric said...

This is the best, simplest explanation I found anywhere. Nice! Thanks!

ZANGELUS said...

Excellent tutorial. Thanks...

Don Larson said...

Thank you.

Bad Monkey said...

Great writeup. I love it when people keep it simple but cover the essentials!

ARod said...

Thanks. Very helpful.