1

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.)

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • This may be the answer, giving it a try: http://stackoverflow.com/questions/7205128/fix-warning-capturing-an-object-strongly-in-this-block-is-likely-to-lead-to-a – Ben Flynn May 01 '13 at 20:27

2 Answers2

2

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
Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
newacct
  • 119,665
  • 29
  • 163
  • 224
  • Added footnote about ARC, since I had to google it understand. – Prof. Falken May 02 '13 at 06:47
  • Does the order of __block and __weak matter? – Ben Flynn May 02 '13 at 14:16
  • 1
    @BenFlynn: no. but `__block` applies to the variable, whereas `__weak` applies to the pointer type – newacct May 02 '13 at 19:38
  • Thanks. I needed to support 4.3, so I am using __unsafe_unretained in place of __weak, but your answer helps clear up what is going on. – Ben Flynn May 02 '13 at 20:42
  • This actually crashes unless you use __unsafe_unretained (at least on iOS 5). – Ben Flynn May 07 '13 at 23:13
  • @BenFlynn: Actually, `successBlock` and `failureBlock`, one of them should have a strong reference to the other. I wasn't sure about the contents of `successBlock` – newacct May 08 '13 at 10:06
  • @newacct Hmm, my code seems to work fine with __unsafe_unretained -- I suppose it could be non-deterministic, but reading http://stackoverflow.com/a/10905056/449161 makes me think it could be OK. – Ben Flynn May 08 '13 at 16:35
0

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.

Community
  • 1
  • 1
Ben Flynn
  • 18,524
  • 20
  • 97
  • 142