Reputation: 715
Ok, this may seem like a dupe question, but if you bear with me and read the whole question, you see why I ask it.
I have seen many nice examples and explanations on the web and here as well, but I just cant get it. Its important for me to understand the whys and the hows, so instead of using some example from the documentation (BTW, I really suck when it comes to understanding Apple Docs, Im used with php.net documentation, with abundance of examples) because then I just don't understand it.
I would like two classes, a gardenClass. It has an array with flowers, each flower is instantiated from the flowerClass. So, the flowerClass does not know what kind of garden instantiated it. It just screams for water sometimes.
So, if someone here could explain this for me, I would be really grateful!
I tried out and here is me having a go based on the info I got from you guys:
MyGarden.h
#import "Flower.h"
@interface MyGarden : NSObject <WateringDelegate>
{
Flower *pinkFlower;
}
@end
MyGarden.m
#import "MyGarden.h"
#import "Flower.h"
@implementation MyGarden
- (void) giveWaterToFlower
{
NSLog(@"Watering Flower");
}
- (void)viewDidLoad
{
pinkFlower = [[Flower alloc] init];
[pinkFlower setDelegate:self];
[pinkFlower startToGrow];
}
@end
Flower.h
@protocol WateringDelegate <NSObject>
@required
- (void) giveWaterToFlower;
@end
@interface Flower : NSObject
{
id <WateringDelegate> delegate;
}
@property (retain) id delegate;
- (void) startToGrow;
@end
Flower.m
#import "Flower.h"
@implementation Flower
@synthesize delegate;
- (void) needWater
{
[[self delegate] giveWaterToFlower];
}
- (void) startToGrow
{
[NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(needWater) userInfo:nil repeats:YES];
}
@end
I have not imported any UIKit or foundation, beause I tried to just do the stuff thats related to delegates here now... So, is this correct?
Upvotes: 3
Views: 2202
Reputation: 8134
As it stands, when the flower wants water it calls self.delegate giveWaterToFlower
.
delegate
is set to MyGarden
by default, so the giveWaterToFlower
method in MyGarden
is called when the flower wants water. If you implemented a wateringCan
object, then wateringCan
could do [pinkFlower setDelegate:self];
Then, when the flower wants water, self.delegate giveWaterToFlower
would call the giveWaterToFlower
method in wateringCan
, which might give more or less water than the default. Similiarly, you could implement a gardenHose
object, that also provides a giveWaterToFlower
method. The gardenhose might only reach a certain distance with the garden, so might only override the delegate in specific flowers.
But the design pattern is that flower
has delegated it's giveWaterToFlower
method to another object. The flower
object has no idea who is providing it's giveWaterToFlower
method. You could include a default giveWaterToFlower
method within flower
and initialise your flower
objects with self.delegate = self;
Upvotes: 0
Reputation: 37985
The delegate pattern is useful in certain cases, as Marc illustrated. The problem is that Apple has used them for much more than they should be used for. In particular, Apple has tried (and failed miserably, imo) to implement a rudimentary event mechanism using delegates.
This goes against the purpose of the delegate pattern, which is to modularize behavior of a class into components. Events do not represent behavior: they represent (often independent) actions that occur dependent upon the event occurring (that is, the action is dependent upon the event, but the effects may be largely independent of the object that raised the event).
The biggest problem with this is that you can have only one delegate handler for an event, whereas there are many valid cases in other languages where we might want to have several independent consumers of an event. In C# for example, the +=
operator is used to add an event handler to an object's event, allowing for an unlimited number of observers of an event. Objective-C unfortunately doesn't have any kind of built-in event mechanism, nor is the event pattern employed by Apple's core framework classes.
Using delegates for events is limiting to say the least. Moreover, the consumer is tightly bound to the producer, as there can be only one consumer. It's therefore not possible (well it's possible, but it wouldn't be wise) for another consumer to change the delegate of a producer.
An example of how Apple uses delegates for what are actually events is the tableView:didSelectRowAtIndexPath
method. That is an event. Why should there only be allowed one thing to be dependent upon a row being selected? The "solution" (though it's more of a workaround) would be for the parent to expose its own delegate with its own pseudo-event delegate method for its child. But that just means more work and more chance of error.
An example of how the delegate model is correctly employed by Apple (insofar as the pattern is concerned), is the tableView:cellForRowAtIndexPath
method. This method allows the behavior of the tableView to be offloaded and defined by a consumer, and it doesn't make sense to provide more than one implementation it.
One good thing about the delegate pattern is that it supports composition equally as well as inheritance. That is, you can derive a controller class that implements its own delegate methods which you can then instantiate, or you can simply compose your class using a generic control and provide the delegate methods for that control yourself.
However, I'm also not a fan of passing the current (parent) object as the delegate of a component (child) object, as this leads to pollution of the class and concept mismatch (the entire class is now providing behavior for just one of its components as opposed to belonging to the concept of the class itself). A better way to do it would be to provide a special mapping object that implements the necessary delegate methods. However, as this is not convenient in Objective-C, the former is unfortunately most often employed by convention.
Upvotes: 0
Reputation: 40507
A delegate is where you implement custom behavior for your app, that affects how another class (such as a plain-old object in the Cocoa framework) does its work.
In other languages that don't use the delegates pattern you might subclass an object to change its behavior. Objective-C lets you do that as well, but the benefit of a delegate is that you don't have to subclass each time you want to make a minor change, an object can be a delegate for multiple other objects.
So consider a controller class that owns a window with some controls, maybe a table of information, a toolbar, etc. You can set the controller to be the delegate of those controls, so that when the controls need more information about what they're supposed to do they can look to your controller. You might want to customize how the window is resized, what happens when the user selects a row in the table view, how information is presented, and so on. Those are all calls the controls make to their delegate (your controller), using methods defined in their delegate protocol. If you're lucky you can write all your custom code without having to subclass and split things up into multiple files.
Another benefit is that you only need to implement the delegate methods you care about. If the default behavior is fine, there's nothing you need to do.
So in your example, the flower class would have some sort of delegate protocol with a "waterMe" method. The delegate (maybe the garden class, or something else altogether) could implement this however you choose, and it would only be called when the flower class needs it to. It's worth noting though that delegation is most useful in frameworks, where you have an object that works in a generic way that may be overridden to accomplish different things. In your own code it's usually more straightforward to add that custom behavior right to your own object, unless you truly are going to be using it in a generic way. In other words, don't be tempted to spend the time making a class that can behave generically when in reality you're only going going to make it behave in one specific way.
Upvotes: 4