17
votes

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.

2
Why is i declared __block anyway? It isn't captured. - Catfish_Man
@Catfish_Man, it is __block int i because it is modified in the next line of code. - krisk
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
@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
@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

2 Answers

5
votes

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/

1
votes

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.