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

Cocoa TouchCocoaAsihttprequestAutomatic Ref-CountingRetain

Cocoa Touch Problem Overview


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.

Cocoa Touch Solutions


Solution 1 - Cocoa Touch

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 - Cocoa Touch

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 - Cocoa Touch

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 - Cocoa Touch

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 - Cocoa Touch

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];        
}];

Solution 6 - Cocoa Touch

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

}];

what the difference between __weak and __block reference?

Solution 7 - Cocoa Touch

Take a look at the documentation on the Apple developer website : https://developer.apple.com/library/prerelease/ios/#documentation/General/Conceptual/ARCProgrammingGuide/Introduction.html#//apple_ref/doc/uid/TP40011029

There is a section about retain cycles at the bottom of the page.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGuillaumeView Question on Stackoverflow
Solution 1 - Cocoa TouchGuillaumeView Answer on Stackoverflow
Solution 2 - Cocoa TouchZaBlancView Answer on Stackoverflow
Solution 3 - Cocoa TouchHDdeveloperView Answer on Stackoverflow
Solution 4 - Cocoa TouchGOrozco58View Answer on Stackoverflow
Solution 5 - Cocoa Touchsquall2022View Answer on Stackoverflow
Solution 6 - Cocoa TouchEmil MarashlievView Answer on Stackoverflow
Solution 7 - Cocoa TouchNyx0ufView Answer on Stackoverflow