Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purposely Create Retain Cycle (Objective C without GC)

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.

like image 747
MikeS Avatar asked May 02 '13 16:05

MikeS


2 Answers

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.

like image 150
BJ Homer Avatar answered Dec 08 '22 11:12

BJ Homer


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.

like image 30
Rob Napier Avatar answered Dec 08 '22 12:12

Rob Napier