17

To better illustrate the question, consider the following simplified form of block recursion:

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        return;
    }
    int i = index;
    next(++i);
};
next(0);

XCode (ARC-enabled) warns that "Capturing 'next' strongly in this block is likely to lead to a retain cycle".

Agreed.

Question 1: Would the retain cycle be successfully broken by setting the block itself to nil, in this fashion:

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        next = nil; // break the retain cycle
        return;
    }
    int i = index;
    next(++i);
};
next(0);

(Note: you'd still get the same warning, but perhaps it is unwarranted)

Question 2: What would be a better implementation of block recursion?

Thanks.

krisk
  • 6,957
  • 1
  • 18
  • 30
  • Why is i declared __block anyway? It isn't captured. – Catfish_Man Feb 12 '13 at 18:58
  • @Catfish_Man, it is `__block int i` because it is modified in the next line of code. – krisk Feb 12 '13 at 18:59
  • This code couldn't possibly work; next isn't assigned until the block is compiled. Thus, the call to next within the block will crash. – Steven Fisher Feb 12 '13 at 19:00
  • @kiro `__block` is only needed if you're modifying a *captured* variable. `i` is local to the block in this case, so no `__block` needed. – BJ Homer Feb 12 '13 at 19:00
  • @StevenFisher, it does compile. – krisk Feb 12 '13 at 19:01
  • @BJHomer (and previous) you're both correct, my bad when I copied from XCode (I had another block within next). I will update the question. – krisk Feb 12 '13 at 19:02
  • Yes, it compiles. But it ought to crash. Try asserting the value of next; it should be nil. – Steven Fisher Feb 12 '13 at 19:03
  • Note that you can also do `int i = i + 1;`, but it isn't semantically correct. – Steven Fisher Feb 12 '13 at 19:04
  • @StevenFisher, it doesn't crash. I'm running it in XCode as I type this. – krisk Feb 12 '13 at 19:05
  • Yup, you appear to be correct. I think it's really unwise to use a variable before it's initialized, but it appears to be possible in this case. – Steven Fisher Feb 12 '13 at 19:06
  • 1
    @StevenFisher: I believe that `next` isn't bound within the Block until it's _excuted_ due to its `__block` storage specifier. bbum's Block Tips and Tricks post has something about this. At any rate, recursive blocks are certainly possible. – jscs Feb 12 '13 at 19:25
  • 3
    @StevenFisher: you are correct that `next` is uninitialized when the block is created. However, `next` is a `__block` variable, which is captured by reference. The `next` seen inside the block reflects the value of `next` when the block executes, which is after the assignment to `next`. – newacct Feb 13 '13 at 03:48

2 Answers2

5

To accomplish the retain-cycle-free recursive block execution, you need to use two block references - one weak and one strong. So for your case, this is what the code could look like:

__block __weak void (^weak_next)(int);
void (^next)(int);
weak_next = next = ^(int index) {
  if (index == 3) {
    return;
  }
  int i = index;
  weak_next(++i);
};
next(0);

Note that the block captures the weak block reference (weak_next), and the external context captures the strong reference (next) to keep the block around. Both references point to the same block.

See https://stackoverflow.com/a/19905407/1956124 for another example of this pattern, which also uses block recursion. In addition, the discussion in the comments section of the following article is relevant here as well: http://ddeville.me/2011/10/recursive-blocks-objc/

Community
  • 1
  • 1
jmkk
  • 261
  • 3
  • 6
1

I think @newacct is correct about @Matt Wilding's solution; it does seem that nothing will have a strong ref to the next block in that case and will result in a run time exception when run (at least it did for me).

I don't know how common it is to find recursively called blocks in the wild in objc. However, in a real world implementation (if actually required) on say, a view controller, one might define the block and then set up an internal interface property with a strong reference to said block:

typedef void(^PushButtonBlock)();

@interface ViewController ()
@property (strong, nonatomic) PushButtonBlock pushButton;
@end

@implementation ViewController
  ... 
  // (in viewDidLoad or some such)
  __weak ViewController *weakSelf = self;

  self.pushButton = ^() {
    [weakSelf.button pushIt];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), weakSelf.pushButton);
  };

  self.pushButton();
  ...
@end

This runs fine for me and has no compiler warnings about retain cycles (and no leaks in instruments). But, I think I would probably steer clear of doing this (recursive block calls) in most cases in objc - it's smelly. But interesting in any case.

boundsj
  • 311
  • 5
  • 3