21

Very similar to this question, I am trying to convert a project that uses ASIHTTPRequest & ASIFormDataRequest to ARC.

In my view controller classes, I often refer to and use properties of the request object in the completion blocks (looking at the response code, response data etc):

__block  ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:SOME_URL]];    
[request setCompletionBlock:^{   

    if([request responseStatusCode] == 200) ....etc

When converting to ARC I get the warning:

Capturing 'request' strongly in this block is likely to lead to a retain cycle

What is the proper way to do this?

Another SO user notes in the previous thread that simply adding __weak may cause the request to be released before the completion of the block, which I believe to be true.

How can I properly reference these properties in completion/failure blocks under ARC?

Community
  • 1
  • 1
barfoon
  • 27,481
  • 26
  • 92
  • 138

3 Answers3

26

(I read your comment to the other question)

After implementing a few more modules using ASIHTTPRequest, I learned that the best way was to keep a strong reference to your request object. In your case, you can do:

self.request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:SOME_URL]];
__weak ASIFormDataRequest *weakRequest = self.request; // __block directive not needed since we only access the instance's properties. 
[self.request setCompletionBlock:^{   

    if([weakRequest responseStatusCode] == 200)
    // ...

This way you can still control self.request even after you start the request (e.g. for cancelling). You can do self.request = nil; when you're ready to release your request, maybe inside your completion block or self.request's parent object's cleanup methods.

Update:

If you're targeting pre-iOS 5, then the common ground stands: use __unsafe_unretained instead of __weak. This is OK because looking at ASIHTTPRequest.m, the blocks are nil'ed out in its dealloc() (i.e. they shouldn't get executed). Although I haven't tested that yet, so make sure to still test with NSZombies enabled.

Note:

The only safe way to cancel an ASIHTTPRequest object is to call its clearDelegatesAndCancel method. I've been bitten by some nasty bugs when I was just using the plain cancel one.

John Estropia
  • 17,460
  • 4
  • 46
  • 50
  • What if you're targeting pre iOS 5? – barfoon Jan 20 '12 at 21:47
  • What if you're creating a set of requests that are being added to an NSOperationQueue? – ktusznio Mar 29 '12 at 04:46
  • 1
    Although I haven't tried this yet, the documentation on `NSOperationQueue`'s ``addOperation` says that the queue retains/keeps a strong reference to the operation. Effectively it should be the same as keeping a `self.request` property. – John Estropia Mar 29 '12 at 07:46
  • This fixed my issue as well. Juan in the comments on another answer described the exact problem I was having where it works fine in the debug but then crashes on the adhoc and would in the release build. Luckily I did the ad hoc to find that problem in a day instead of it taking an entire approval cycle. – badweasel Nov 07 '13 at 07:23
2

If you're targeting iOS versions before 5.0, that do not include weak support:

__unsafe_unretained __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
magma
  • 8,432
  • 1
  • 35
  • 33
  • see also: http://stackoverflow.com/questions/7205128/fix-warning-capturing-an-object-strongly-in-this-block-is-likely-to-lead-to-a (possible duplicate) – magma Jan 15 '12 at 00:03
  • 2
    This method does NOT work well, at least on iOS 5.0+. For some reason app would launch on simulator and on device if launched from xcode, however if it was installed using an adhoc distribution (such as TestFlight) it would crash as soon as app is launched. Use method suggested by accepted answer instead! – Juan Feb 26 '13 at 20:50
  • barfoon, the question appears as linked _because_ I linked to it. – magma Feb 27 '13 at 00:45
  • magma.. Juan is absolutely correct. this method does NOT WORK on ios5+. It does the exact same thing to me and I came here trying to find an answer. My ad hoc version was suddenly crashing, while the debug version doesn't. And it wasn't easy but I tracked it down to this exact line of code. In this, "request" immediately gets released and then causes a bad access when trying to do anything with it. But only in the release version, not in the debug build. – badweasel Nov 07 '13 at 07:12
  • @badweasel this works perfectly to answer the question asked by the op, which was "referencing request object within blocks under ARC", when using an iOS version lacking __weak support. The other answer uses a __weak reference, that older iOS versions do NOT support. This is the way to do it. OF COURSE you have to be aware that you cannot rely on the fancy __weak mechanism and if you don't know how to handle the pointer properly, you will get a crash. I'm sorry to say that this is your fault for not properly managing the pointer, not my answer's fault. Downvoting is inappropriate. – magma Nov 07 '13 at 11:14
  • I'm amazed at how many people don't know about pointers, pretend that they can work with Objective-C without knowing them, and are ready to jump at the chance to blame someone else for their crashes. Do your homework, gentlemen. – magma Nov 07 '13 at 11:31
  • Yes but the fact is that xcode apparently uses different retain rules when building a debug version or building a distribution version. If I had time to write a test app that proves it I would submit it as a bug report to apple. But I don't. Still, this is very important to know because you can write an app that works correctly in testing and then crashes on submission. My app had no memory leaks on testing the debug build. But then crashes when accessing `request` in the ad hoc. Neither have a leak, but one retains the variable longer than the other. – badweasel Nov 07 '13 at 23:56
  • My problem with your comment to Juan was that you had slammed him for pointing out that it's not a solution for iOS5, stating that he shouldn't blame his inability to debug on this... which is not constructive - considering that he was correct. His comment helped point me in the right direction on my problem. What's great about SO is that questions live forever but the answers can become more correct and more relevant over time, instead of less and less. I don't see how it is blaming some else for your crash to come to SO and look for solutions, or to point out the way things really work. – badweasel Nov 08 '13 at 00:00
1

I've found this answer to be helpful: https://stackoverflow.com/a/7735770/133875

It says to use __unsafe_unretained as well as __block

Community
  • 1
  • 1
Stephen Eilert
  • 1,527
  • 1
  • 17
  • 17