Is there ever a case where purposely creating a retain cycle to prevent deallocation, then cleaning it up later, is the best solution to a problem?
If so, are there examples of this in the Cocoa Touch or NextStep frameworks?
I intend this question to be specific to Objective C with ARC since Objective C with GC or other languages with GC may behave differently.
Sure. It's actually not all that uncommon, though you may not realize it.
For example, let's assume that my controller is making a network request, and I really need to make sure I handle the response, even if the user has navigated away from that controller.
I might do something like this:
- (void)doNetworkThing {
__block MyController *blockSelf = self;
NSURLRequest *request = // some request
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:
^(NSURLResponse *response, NSData *data, NSError *error) {
// Handle the response here
[blockSelf doThingWithResponse:response];
}];
}
This introduces a trivial retain cycle where self
has caused itself to be retained by assigning itself to the strong pointer blockSelf
. Until blockSelf
goes out of scope, self will not be deallocated.
Note that often, you would use a weak pointer in this situation. But if you really need the controller to be around to handle it, using a strong pointer works too. As soon as the handler block is deallocated, its reference to blockSelf
will go away. Since the the stack reference to blockSelf
is also gone self will then be deallocated if nobody else is holding on to it.
So basically, blockSelf
caused a temporary retain cycle, which was useful in ensuring that deallocation could not happen until the request finished. Because ARC automatically cleans up the retain counts when the __block variable goes out of scope, it doesn't look much like a retain cycle. But nevertheless, that's what it is.
Sure, there are several. Most notably NSURLConnection
generates a retain loop with its delegate to ensure that the delegate cannot go away before the connection completes. Somewhat less ideally, NSTimer
generates a retain loop with its target. This unfortunately causes problems for repeating timers (see RNTimer for one of many attempts to work around this issue).
Generally speaking, if a delegating object has a very short lifespan compared to its delegate, it is natural for it to retain its delegate in order to create a beneficial retain loop.
It is much less common, but still done, for certain kinds of objects to "self retain." For example, if you had an object that you wanted to create and have some operation perform, and then destroy itself, then self retain can solve that problem. Typically it is better to avoid that and let the caller retain the object, but it still has its uses (I've used it for logging and other non-critical operations that I want to fire-and-forget).
That said, it is not uncommon in my code to intentionally create short-lived retain loops with blocks to ensure that the calling object cannot vanish before the block finishes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With