6

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.

MikeS
  • 3,891
  • 6
  • 35
  • 51
  • Wouldn't a better solution be to keep a strong reference using an ivar or other appropriate variable? – rmaddy May 02 '13 at 16:52
  • Common knowledge would say yes. But that is why I asked the question. Perhaps there is a case where creating a retain cycle (such as one where an object retains itself) is the best solution. – MikeS May 02 '13 at 16:53
  • 1
    This is a difficult question to answer because it is too open ended. Questions with "ever" or "never" and "best" can't be answered well. It would be better if you had a concrete use case to ask about. – rmaddy May 02 '13 at 16:57
  • I could simply refine it to "Do the NextStep or Cocoa Touch libraries ever purposely create retain cycles? If so, why?" if need be... But I don't think the question is too open ended personally. But that would be a question with a definite yes or no answer I guess... – MikeS May 02 '13 at 16:58

2 Answers2

7

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.

BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • 4
    While the block-retains-self example is good, you should never be doing network access inside of view controllers specifically since they may go away at any time. You should only do network access in model objects (or model controllers) that have long lifespans. This is much easier to test and avoids many annoying corner cases. – Rob Napier May 02 '13 at 17:34
  • @BJHomer, good example. I hadn't thought of this scenario. I'll just go ahead and delete my response because it's just plain wrong. – aLevelOfIndirection May 02 '13 at 21:52
1

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.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610