Fix warning "Capturing [an object] strongly in this block is likely to lead to a retain cycle" in ARC-enabled code

47,646

Solution 1

Replying to myself:

My understanding of the documentation says that using keyword block and setting the variable to nil after using it inside the block should be ok, but it still shows the warning.

__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    request = nil;
// ....

    }];

Update: got it to work with the keyword '_weak' instead of '_block', and using a temporary variable:

ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    // ...
    }];

If you want to also target iOS 4, use __unsafe_unretained instead of __weak. Same behavior, but the pointer stays dangling instead of being automatically set to nil when the object is destroyed.

Solution 2

The issue occurs because you're assigning a block to request that has a strong reference to request in it. The block will automatically retain request, so the original request won't deallocate because of the cycle. Make sense?

It's just weird because you're tagging the request object with __block so it can refer to itself. You can fix this by creating a weak reference alongside it.

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];

Solution 3

It causes due to retaining the self in the block. Block will accessed from self, and self is referred in block. this will create a retain cycle.

Try solving this by create a weak refernce of self

__weak typeof(self) weakSelf = self;

operationManager = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

    [weakSelf requestFinishWithSucessResponseObject:responseObject withAFHTTPRequestOperation:operation andRequestType:eRequestType];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [weakSelf requestFinishWithFailureResponseObject:error withAFHTTPRequestOperation:operation andRequestType:eRequestType];
}];
[operationManager start];

Solution 4

Some times the xcode compiler has problems for identifier the retain cycles, so if you are sure that you isn't retain the completionBlock you can put a compiler flag like this:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"

-(void)someMethod {
}

Solution 5

When I try the solution provided by Guillaume, everything is fine in Debug mode but it crashs in Release mode.

Note that don't use __weak but __unsafe_unretained because my target is iOS 4.3.

My code crashs when setCompletionBlock: is called on object "request" : request was deallocated ...

So, this solution works both in Debug and Release modes :

// Avoiding retain cycle :
// - ASIHttpRequest object is a strong property (crashs if local variable)
// - use of an __unsafe_unretained pointer towards self inside block code

self.request = [ASIHttpRequest initWithURL:...
__unsafe_unretained DataModel * dataModel = self;

[self.request setCompletionBlock:^
{
    [dataModel processResponseWithData:dataModel.request.receivedData];        
}];
Share:
47,646
Guillaume
Author by

Guillaume

Updated on November 27, 2020

Comments

  • Guillaume
    Guillaume over 3 years

    In ARC enabled code, how to fix a warning about a potential retain cycle, when using a block-based API?

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

    produced by this snippet of code:

    ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
    
    [request setCompletionBlock:^{
        NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.rawResponseData error:nil];
        // ...
        }];
    

    Warning is linked to the use of the object request inside the block.

  • Guillaume
    Guillaume almost 13 years
    Still would like to understand why the first solution doesn't work.
  • Hunter
    Hunter over 12 years
    Based on the ARC docs, sounds like you need to use __unsafe_unretained __block together to get the same behavior as before when using ARC and blocks.
  • Sean Clark Hess
    Sean Clark Hess over 12 years
    You can combine the first two lines: __weak ASIHTTPRequest * request = [[ASIHTTPRequest alloc] ...
  • Guillaume
    Guillaume over 12 years
    @SeanClarkHess : When I combine the first two lines, I get this warning: "Assigning retained object to weak variable; object will be released after assignment"
  • Chris Wagner
    Chris Wagner over 12 years
    I am getting he same thing @Guillaume, did you find a solution?
  • Guillaume
    Guillaume over 12 years
    @ChrisWagner My code after "Update: got it to work" compiles without warning. What is your problem?
  • Chris Wagner
    Chris Wagner over 12 years
    @Guillaume thanks for the response, some how I overlooked the temporary variable, tried that and the warnings are gone. Do you know why this works? Is it just tricking the compiler to suppress the warnings or is the warning actually no longer valid?
  • John Estropia
    John Estropia over 12 years
    Be careful with the second code. The request weak variable inside your completion block will most likely be nil by the time your block executes. I haven't found an elegant solution to this as well though, so I'm just ignoring the warning for now..
  • barfoon
    barfoon over 12 years
    @erurainon After seeing your response I am wondering now if the "Update: got it to work" is actually the proper way to do this. How can you safely refer to and use the request object within the block here?
  • barfoon
    barfoon over 12 years
    I've posted a follow up question: stackoverflow.com/questions/8859649/…
  • Valerio Santinelli
    Valerio Santinelli over 11 years
    Interesting solution. Did you figure out why it crashes in Release mode and not in Debug?
  • user798719
    user798719 over 11 years
    Can someone explain why you need the __block and __weak keywords? I guess there is a retain cycle being created, but I do not see it. And how does creating a temporary variable fix the problem?
  • Benjamin
    Benjamin over 9 years
    This is the correct answer and should be noted as such
  • hyperspasm
    hyperspasm over 9 years
    Some might argue that it is bad design but I sometimes create independent objects that hang out in memory until they are finished with an asynchronous task. They are retained by a completionBlock property which contains a strong reference to self, creating an intentional retain cycle. The completionBlock contains self.completionBlock=nil, which releases the completionBlock and breaks the retain cycle, allowing the object to be released from memory once the task is complete. Your answer is useful to help quiet the warnings that occur when I do this.
  • Max MacLeod
    Max MacLeod over 8 years
    to be honest, chances of one being right and the compiler being wrong are very small. So I'd say just surpassing the warnings is risky business
  • Ram G.
    Ram G. almost 6 years
    __weak ASIHTTPRequest *wrequest = request; did not work for me. Giving error I used __block ASIHTTPRequest *blockRequest = request;