Why are Objective-C delegates usually given the property assign instead of retain?
Asked Answered
C

4

176

I'm surfing through the wonderful blog maintained by Scott Stevenson, and I'm trying to understand a fundamental Objective-C concept of assigning delegates the 'assign' property vs 'retain'. Note, the both are the same in a garbage collected environment. I'm mostly concerned with a non-GC based environment (eg: iPhone).

Directly from Scott's blog:

"The assign keyword will generate a setter which assigns the value to the instance variable directly, rather than copying or retaining it. This is best for primitive types like NSInteger and CGFloat, or objects you don't directly own, such as delegates."

What does it mean that you don't directly own the delegate object? I typically retain my delegates, because if I don't want them to go away into the abyss, retain will take care of that for me. I usually abstract UITableViewController away from its respective dataSource and delegate also. I also retain that particular object. I want to make sure it never goes away so my UITableView always has its delegate around.

Can someone further explain where/why I'm wrong, so I can understand this common paradigm in Objective-C 2.0 programming of using the assign property on delegates instead of retain?

Thanks!

Cheeky answered 28/5, 2009 at 0:57 Comment(2)
Retagged with "delegates" and without "iphone".Ambert
why is delegate assign instead of copy (like NSString?)Presumptuous
S
175

The reason that you avoid retaining delegates is that you need to avoid a retain cycle:

A creates B A sets itself as B's delegate … A is released by its owner

If B had retained A, A wouldn't be released, as B owns A, thus A's dealloc would never get called, causing both A and B to leak.

You shouldn't worry about A going away because it owns B and thus gets rid of it in dealloc.

Shearwater answered 28/5, 2009 at 1:10 Comment(5)
I don't agree, Mike. I just found a problem where a modal has a delegate that dismisses the modal. But when I do a memory warning in the modal, it releases the delegate. Then when I go to dismiss my modal, the delegate is nil. Crash.Nervous
Ok so I disagree, but you're right that it's a design flaw. I found out that I was passing on my dismiss call through a child class of the real dismisser. That child class got released and couldn't pass onto the modal's container delegate to dismiss. I changed it to pass on a pointer to the final delegate and that one didn't get released at memory warning and all is good.Nervous
Your code should never be written in such a way that a nil delegate causes it to crash. Only the owning object should have an owning reference. When dealloc'd, it must set the delegates of owned objects to nil before releasing them. Then any messages sent to the nil delegate are simply ignored. Passing a nil object in a message can crash, however. Just make sure you don't deal with delegates that way.Lantz
Wait -- isn't that what weak does? The question is why use assign instead of weak?Rostellum
@wcochran: No, this question is why use assign instead of retain. The question is older than ARC; weak and strong (the latter being synonymous with retain) didn't exist until ARC was introduced. You should ask your question about weak vs. assign separately.Emlyn
E
45

Because the object sending the delegate messages does not own the delegate.

Many times, it's the other way around, as when a controller sets itself as the delegate of a view or window: the controller owns the view/window, so if the view/window owned its delegate, both objects would be owning each other. This, of course, is a retain cycle, similar to a leak with the same consequence (objects that should be dead remain alive).

Other times, the objects are peers: neither one owns the other, probably because they are both owned by the same third object.

Either way, the object with the delegate should not retain its delegate.

(There's at least one exception, by the way. I don't remember what it was, and I don't think there was a good reason for it.)


Addendum (added 2012-05-19): Under ARC, you should use weak instead of assign. Weak references get set to nil automatically when the object dies, eliminating the possibility that the delegating object will end up sending messages to the dead delegate.

If you're staying away from ARC for some reason, at least change assign properties that point to objects to unsafe_unretained, which make explicit that this is an unretained but non-zeroing reference to an object.

assign remains appropriate for non-object values under both ARC and MRC.

Emlyn answered 28/5, 2009 at 1:11 Comment(4)
NSURLConnection retains its delegate.Silky
Yes, use weak, but that doesn't answer the original question: Why does Apple use assign instead of weak?Rostellum
@wcochran: The original question was “why are delegate properties given assign rather than retain ”; weak didn't exist when it was asked. Your question is a different one, which you should ask separately. I'd be happy to answer it.Emlyn
@Rostellum and Peter, has that question been asked somewhere else?Talion
B
18

Note that when you have a delegate that's assign, it makes it very important to always set that delegate value to nil whenever the object is going to be deallocated - so an object should always be careful to nil out delegate references in dealloc if it has not done so elsewhere.

Biddy answered 28/5, 2009 at 15:17 Comment(4)
“Note that when you have a delegate that's assign, it makes it very important to always set that delegate value to nil whenever the object is going to be deallocated” Why?Emlyn
Because any reference left set, will be invalid after an object is deallocated (pointing at memory no longer allocated to the kind of object expected) - and thus cause a crash if you try to use it. A sign of this in the debugger is when the debugger claims some variable has a type that seems totally wrong from what the variable is actually declared as.Biddy
This is only necessary if the object that you are a delegate of is being retained by another source, such as a timer or other asynchronous callback. Otherwise, it will be dealloced after you release it and won't be trying to call delegate methods.Shearwater
@Andrew: That's true, but if you always make it a practice to nil out delegates then you won't forget when it matters, or if you accidentally over-retain a held object and it stays around anyway. If you nil out the delegate then the result is just a leak, instead of a leak followed by a crash.Biddy
E
1

One of the reason behind that is to avoid retain cycles. Just to avoid the scenario where A and B both object reference each other and none of them is released from memory.

Acutally assign is best for primitive types like NSInteger and CGFloat, or objects you don't directly own, such as delegates.

Elga answered 28/12, 2015 at 10:1 Comment(1)
That's rather copied from the OP's quote and the accepted answer respectively, isn't it?Collective

© 2022 - 2024 — McMap. All rights reserved.