views:

343

answers:

4

I'm using the delegate pattern for one of my objects. My idea is that I will be able to swap the delegate out later for a different delegate implementing a different strategy. I suppose this is just as much the strategy pattern as the delegate pattern.

My question is, is it bad practice for my delegate to have a reference back to the object for which it is the delegate? There are a couple properties of that object that I need to have access to regardless of which delegate/strategy I use. If this is bad form, how should I get access to the properties?

+7  A: 

Not necessarily. For example, it's quite common for a "controller"-type object to create data objects and views and make itself the delegate of some of those objects in order to properly control things.

Chuck
A: 

I'd say yes, it's bad practice. The idea behind a delegate is that it's effectively a standalone object that receives messages about the object for which it is the delegate (the "delegator"). The delegator should have a reference to the delegate, not the other way around, otherwise it's not a true delegation relationship anymore.

A preferred way to accomplish what you're asking is to provide the sending object along with whatever message your delegate receives. For example, on your delegate, instead of having a delegator property and then receiving, for example, a didDoSomething:(id)anObject method, you could remove the delegator property and send the message delegator:(id)anObject didDoSomething:(id)anotherObject. This way, you keep the delegate distinct from the delegator, but still get access to the delegator's properties when you need them.

This way also has the advantage of not providing access to the delegator in methods when you don't truly need it; for example, your delegate could have a didDoSomething method that takes no arguments, not even a delegator, and is just used for logging, as well as a delegator:(id)anObject didSomethingElse:(id)anotherObject method that calls some properties on the delegator and is much more involved.

Finally, this method allows you to use the same delegate for multiple delegators, since you don't need to update a delegator property for each delegate object.

For a good example of how this works, take a look at the NSURLConnection documentation, specifically its delegate methods - a lot of them take the form connection:didDoSomething:, where the first argument is the connection calling the delegator. Developers commonly define a single connection delegate for multiple connections, implementing their delegate methods to do different things depending on properties of the NSURLConnection object passed in.

Tim
Downvoted because...?
Tim
I don't know why you were downvoted, but I like your answer. Thanks for the thorough explanation and pointer to NSURLConnection.
scompt.com
Your post seems to assume the delegating class was designed around the assumption that the delegate would have a reference to it. There's no reason for this. For example, it's quite common in non-bindings projects for a controller to have a reference to a table view and also be that table view's delegate, implementing the normal delegate methods.
Chuck
Using the NSURLConnection, as an implementor you always keep track of your NSURLConnections externally (one or a collection of them). You do this if for no other reason so you can gracefully cancel the connections if some external force demands it (screen locking or memory warning might be some good examples). The API passes back the connection used not so that you don't need to keep a reference around, but because it's typical to use multiple NSURL connections against a single delegate class and this is the only way to distinguish which connection responded.
Kendall Helmstetter Gelner
One other really good reason why you would keep a reference to any open NSURLConnections around - what happens if your class is released? If you don't remove yourself as a delegate of the connection then when the call returns the NSURLConnection object will attempt to make a delegate call against a released object.
Kendall Helmstetter Gelner
Downvoted because I disagree with first sentence. And later: "A preferred way to accomplish what you're asking is to provide the sending object along with whatever message your delegate receives" This is just extra work, requires casting values, yuk.
Frank Schwieterman
@Frank: About the "provide the sending object," I think he means the way delegates always do. Like `textShouldBeginEditing:` passes the sending text control as an argument. It's really no extra trouble, and it's already done universally.
Chuck
I'm somewhat mystified by the down-voting, as well. Certainly, passing the delegating object as a parameter to the delegate methods is a common pattern in UIKit and AppKit - UITableView does it, NSURLConnection does it, the various UIControl subclasses do it... Without some indication of the object requesting the delegation, you can't use the same object as a delegate for more than one client, which is commonly done.
Mark Bessey
@Frank: I don't think any casts are required - with some careful application of `@class` and `#import` statements, you can actually build a formal protocol definition for a delegate and implement it with a class such that the first argument is of the proper type, not just `id`. I only used `id` because I'm not sure exactly what the OP is delegating.
Tim
@Mark: Nobody is downvoting the idea of passing self to a delegate method. Everybody agrees you should do that. The downvotes are for saying that a controller should not keep a reference to an object for which it is the delegate.
Chuck
+2  A: 

Generally a dependency should not have a reference back to the dependent object, as this would be a classical circle reference. To avoid having a back-reference you can supply the needed properties as arguments in a delegate method, or you move the properties themselves into the delegate.

Ozan
A thorough discussion of this here:http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html
Felixyz
+2  A: 

It is not at all bad practice. The delegate pattern allows for a class to have a generic way to talk to any number of objects as long as the implement the same protocol. But the class on which you set the delegate also will usually have a number of public properties or methods that allow you to query or alter what the class is doing, in response to which the class may in turn trigger a number of the delegate calls. So you need a reference of the class to which you are the delegate, in order to tell the object to do something different than it is already, and of course to release it when you are through with it!

This is also why it's important to always have any delegate properties as assign, and not retain, properties. That way when the originating class is released it will actually be released, instead of having delegate objects it is holding onto causing a retain loop that keeps both around.

It's also why whenever you are being released, you should set the delegate reference to nil in anything you may have a delegate reference set. This way a class will not have an invalid delegate reference if the delegate is released before the class that uses a delegate.

Kendall Helmstetter Gelner
What is a "release property" (as opposed to a retain property)? I think you means "delegate properties as assign, not retain."
Chuck
Wow, that was a really bad typo. I fixed it, thanks!
Kendall Helmstetter Gelner