Ben Flynn
Ben Flynn

Reputation: 18922

Block passing itself as argument to method causing compiler warning

I was trying to create a callback that retries the method after a delay on failure. I'm hitting this warning:

"Capturing failure block strongly in this block is likely to lead to a retain cycle."

typedef void (^MyCallbackBlock)(NSObject *);

...

__block MyObject *blockSelf = self;
__block MyCallbackBlock successBlock = ^(NSObject *someObject)
{
    // To be completed
};
__block MyCallbackBlock failureBlock = ^(NSObject *someObject)
{
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [blockSelf doSomething:someObject onSuccess:successBlock onFailure:failureBlock]; // <-- Warning is here
    });
};
[blockSelf doSomething:someObject onSuccess:successBlock onFailure:failureBlock];

...

- (void)doSomething:(NSObject *)someObject
          onSuccess:(MyCallbackBlock)successBlock
          onFailure:(MyCallbackBlock)failureBlock;

The question: How can I make this work properly?

(I've been reading through other SO questions -- haven't found a match yet, though wouldn't be surprised if one is out there.)

Upvotes: 1

Views: 196

Answers (2)

Ben Flynn
Ben Flynn

Reputation: 18922

Adding __unsafe_unretained worked, as in:

__unsafe_unretained __block MyCallbackBlock successBlock = ^(NSObject *someObject)
{
    // To be completed
};

While it seemed possible that __weak could work, in practice it caused my application to crash. It's not 100% clear that this answer explains the reason, but I'm imagining it's something along those lines.

Upvotes: 0

newacct
newacct

Reputation: 122439

Yes, the block needs to capture itself (as well as self) as a weak reference.

If you're using ARC*, it should be like this:

MyObject *__weak blockSelf = self;
__block __weak MyCallbackBlock weakSuccessBlock;
MyCallbackBlock successBlock = weakSuccessBlock = ^(NSObject *someObject)
{
    // To be completed
};
__block __weak MyCallbackBlock weakFailureBlock;
MyCallbackBlock failureBlock = weakFailureBlock = ^(NSObject *someObject)
{
    MyCallbackBlock strongSuccessBlock = weakSuccessBlock;
    MyCallbackBlock strongFailureBlock = weakFailureBlock;
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [blockSelf doSomething:someObject onSuccess:strongSuccessBlock onFailure:strongFailureBlock];
    });
};

If you're not using ARC, replace the __block __weak and __weak above with just __block.


*: Objective-C Automatic Reference Counting

Upvotes: 2

Related Questions