OK to retain ASIHTTPRequest delegate?

2020-05-08 00:34发布

问题:

Is it OK to retain a delegate of a subclass of ASIHTTPRequest?

I made a subclass of ASIHTTPRequest called JSONRequest. Each instance of JSONRequest is its own delegate, handles the callbacks, and passes them on to jsonDelegate, which is a private property of JSONRequest, and responds to requestFinished:withResult:, where result is an NSDictionary representation of the JSON response. To do this, I overloaded setDelegate: in JSONRequest to do super.delegate = self; self.jsonDelegate = newDelegate.

Is it OK to retain jsonDelegate in this case because often jsonDelegate is a view controller, which is sometimes dealloced while the request is loading if the user hits "Back," etc. I will release jsonDelegate in JSONRequest after calling the callback methods.

How do I know this is OK and will not cause a retain loop.

回答1:

If your release is deterministic (if you promise it will always happen, even in the case of an error, a timeout, or some other sort of unexpected event - like the connection simply hanging indefinitely) - then this is fine (no retain loop).

In this case, the retain loop would always get broken at some point.



回答2:

What's retaining the request? (The operation queue, perhaps? Who knows?)

In general, the "fire-and-forget-and-give-me-a-callback" method you seem to be proposing is a bad idea. If nothing is retaining the VC apart from the request, then (unless your app structure is a bit silly) the VC will never get to do anything with the data it receives, so there's no reason for it to continue.

It also feels wrong: Does the request own the VC, or does the VC own the request? I'd expect the latter, so the VC should also retain the request.

There are a couple of exceptions:

  • CAAnimation.delegate is retained, presumably because the animation is going to complete at some point (I'm not sure what happens if it's a repeating animation). The same may be true of UIKit's animation delegate
  • NSTimer retains its target, probably because NSInvocation does. (I wrote a "weak timer" class to work around this.)
  • CADisplayLink retains its target, presumably to be like NSTimer.

For these cases, I often work around it with a "weak proxy" class which doesn't retain its target (and I wrote a "weak timer" wrappers around NSTimer/CADisplayLink to make this a bit easier.)

What you're supposed to do is keep track of the requests that you've initiated and in dealloc, do something like

request.delegate = nil;
[request cancel];
self.request = nil;

Similarly, you're supposed to unregister for notifications/actions/KVO callbacks at the appropriate time. There's an exception:

  • If you're sure that it won't send you a callback after you release it, you don't need to bother, so text field delegates and button action/targets don't need to be cleared.

There are exceptions to the exception:

  • UIWebView (at least in older OSes) are also retained by something else, possibly something to do with the web thread. It can crash if the VC disappears while the web view is still loading.
  • UIScrollView scrolling callbacks also cause the view to be retained past the lifetime of the VC. You can test this by e.g. holding "Done" and starting a flick as you release "Done".


回答3:

I know that this question is originally about ASIHTTPRequest, but people might stumble upon this thread and it might make them think that using ASIHTTPRequest is still best practice - it is not, infact development has stopped in 2011 AFAIK.

Using more modern HTTP libraries which use blocks by default (I prefer AFNetworking), all variables referenced in fail/success blocks are either copied or retained until the blocks are released. This will spare you this whole memory management headache - except if you use self in one of the blocks, then you will create a retain cycle until the blocks have been released.