-
-
Save BigZaphod/1101862 to your computer and use it in GitHub Desktop.
// say that the object which is our _delegate is the only object retaining us | |
// and it calls this -processStuff method… | |
- (void)processStuff | |
{ | |
// and then this happens: | |
[_delegate finishedSomething:self]; | |
// now, in _delegate's -finishedSomething: it ends up releasing us like so: | |
// [_processorOfStuff release]; | |
// if that delegate object was the only owner, do we get destroyed now? | |
// prior to ARC and assuming we're not in an autorelease pool, I think yes. | |
// so what happens when the -finishedSomething: method is done and we | |
// come back here and attempt: | |
[self doSomeFollowupStuff]; | |
// do we crash and burn here? prior to ARC, the answer should be be yes | |
// if we're not in an autorelease pool, etc. | |
// so the fixes I would normally consider would be, if I could anticipate | |
// my delegate might trash me, I could put a [self retain]/[self autorelease] | |
// in our implementation of -processStuff here, or I might do a | |
// [_processorOfStuff autorelease] instead of release in _delegate's | |
// -finishedSomething: method to avoid the trouble. | |
// what is the best practice solution for this situation? | |
// it seems unreasonable to be able to perfectly anticipate what's going | |
// to happen to us while calling out to a delegate we might not control | |
// so guarding such calls with a [self retain]/[self autorelease] seemed | |
// okay but ARC bans the use of retain/release/autorelease, so does ARC | |
// solve this for us somehow (maybe by ensuring we don't die while executing | |
// a method) or is there something I'm missing? | |
} |
I don't think ARC magically fixes this unless when you pass self
to finishedSomething:
it actually does a retain/autorelease (which it might do, but that also might be subject to the autorelease squashing that ARC does).
You could work around this by creating a reference to self
at the beginning:
- (void)processStuff
{
id strongSelf = self;
...
[_delegate finishedSomething:self];
...
[self doSomeFollowupStuff];
// ARC will release strongSelf down here somewhere
}
That'd be sweet if it worked, are there any guarantees that the optimizer won't kill that?
Yeah, you should probably actually use strongSelf
later on:
- (void)processStuff
{
id strongSelf = self;
...
[_delegate finishedSomething:self];
...
[strongSelf doSomeFollowupStuff];
// ARC will release strongSelf down here somewhere
}
(This is all untested, but it's essentially the same as the example in the WWDC ARC presentation of keeping a reference to self alive through the entirety of a block.)
Ah, sweet. (I need to go back and re-watch that presentation).
Did they mention how to force a delayed release? I find this useful on occasion:
[[self retain] autorelease];
They mentioned in the video that an optimizer pass will attempt to pull out "extra" retain/releases inserted by ARC -- it seems that ARC is going to be inserting the retain/release calls before the optimizer runs. So, strongSelf should maintain a +1 until some point between it's last use and the end of it's scope. This would make the first suggestion sorta iffy, since it'd be dependent upon exactly where the compiler decides to put the release. The pattern in the second suggestion should be perfectly valid, though.
I ran into this exact problem where I want to keep a reference to self
alive for a bit longer than ARC thinks it needs to. I didn't try putting a local variable into the function because my pattern is that a UIAlertView is shown and I need to delay the release until the user taps one of the buttons.
Adding a new instance variable to the class and assigning self
to it, then nil'ing it out later is sufficient to outsmart ARC.
I take it is impossible in your specific situation to just call the delegate method after doSomeFollowupStuff?
(I am still interested in how ARC would handle the situation you describe, but if the above works as well, why not circumvent the entire situation?)