How to fix "Capturing 'block' strongly in this block is likely to lead to a retain cycle"

asynchronous, ios, objective-c, objective-c-blocks, retain-cycle

Solution

WARNING: Block pointer variable 'completionBlock' is uninitialized when captured by the block

This happens because block variables initialized to a recursive block need `__block` storage.

- Variables within a block are copied unless declared with `__block`, in which case they are passed as reference.

- When a recursive block is assigned to a block variable, the creation happens before the assignment, and such creation triggers a variable copy. Given that the variable hasn't been assigned yet, the copied variable will be a bad value, and it will produce a crash when the block is ran.

- But if we add `__block`, the block will be created with a reference to the variable instead. Then the variable will be initialized to the created block, and the block will be ready to use.

WARNING: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle

This happens because a block variable is a strong reference to the block, and the block is itself referencing the variable (because as we saw before, the variable has a `__block` so it is referenced instead copied).

So we need

- A weak reference to the strong variable inside the block.

- And a strong reference outside to prevent the block from being deallocated during the scope of the method where it is created.

    void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request);
    void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request);
    weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){
        [self lengthyAsyncMethod:weakCompletionBlock];
    };

The name `doLengthyAsynchronousOperationWithCompletionBlock` suggests that the method may outlive the method scope where the block is created. Given that the compiler doesn't copy a block passed as an argument, it's responsibility of this method to copy this block. If we are using this block with block aware code (eg: `dispatch_async()`), this happens automatically.

Had we been assigning this block to an instance variable, we would need a `@property(copy)` and a weak reference to self inside the block, but this is not the case, so we just use self.

Problem

I am working on this code, which does some lengthy asyncronous operation on the net and when it finishes it triggers a completion block where some test is executed and if a variable get a certain value another lengthy operation should start immediately: ``` -(void) performOperation { void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){ int variable=0; // Do completion operation A //... //... // Do completion operation B //Get the variable value if(variable>0){ [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock]; } }; //Perform the lenhgty operation with the above completionBlock [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock]; } -(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock { //Do some lengthy asynchronous stuff } ``` With this code I get this warning from the compiler: ``` WARNING: Block pointer variable 'completionBlock' is uninitialized when caputerd by the block ``` I changed: ``` void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request) ``` in: ``` __block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request) ``` but I get this other warning: ``` WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle ``` How can I fix this? Thanks Nicola

Original source

Related problems