块递归和打破保留循环。

17
为了更好地说明问题,考虑以下简化形式的块递归:
__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        return;
    }
    int i = index;
    next(++i);
};
next(0);

XCode(启用ARC)警告:“在此块中强引用‘next’可能会导致保留循环”。

同意。

问题1:通过将块本身设置为nil,可以成功打破保留循环吗?如下所示:

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

(注:您仍将收到相同的警告,但这可能是不必要的)

问题2:块递归的更好实现方法是什么?

谢谢。


为什么i被声明为__block?它并没有被捕获。 - Catfish_Man
这段代码不可能正常工作;在块编译之前,next没有被分配。因此,在块内调用next将会导致崩溃。 - Steven Fisher
是的,你似乎是正确的。我认为在变量初始化之前使用它真的很不明智,但在这种情况下似乎是可能的。 - Steven Fisher
1
@StevenFisher:我相信next在块内部直到由于其__block存储说明符而被执行时才会绑定。bbum的“Block Tips and Tricks”帖子中有关于此的内容。无论如何,递归块肯定是可能的。 - jscs
3
@StevenFisher说得对,当块被创建时,“next”未初始化。然而,“next”是一个“__block”变量,它被引用捕获。块内部看到的“next”反映了块执行时的“next”值,这是在赋值给“next”之后发生的。 - newacct
显示剩余7条评论
2个回答

5
为了实现无保留循环递归块执行,您需要使用两个块引用 - 一个弱引用和一个强引用。因此,对于您的情况,代码可能如下所示:
__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);

请注意,块捕获弱块引用(weak_next),外部环境捕获强引用(next)来保持块的存在。这两个引用指向同一个块。
请参见另一个例子 https://dev59.com/UGIj5IYBdhLWcg3wx38x#19905407,它也使用了块递归。此外,在以下文章的评论部分中讨论的内容也与此相关:http://ddeville.me/2011/10/recursive-blocks-objc/

1
我认为@newacct对于@Matt Wilding的解决方案是正确的;在这种情况下,似乎没有任何东西会对下一个块有强引用,并且在运行时会导致运行时异常(至少对我来说是这样)。
我不知道在objc中递归调用块有多常见。然而,在实际的实现中(如果确实需要),例如在视图控制器上,可以定义该块,然后设置一个内部接口属性,其中包含对该块的强引用:
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

这对我来说运行良好,并且没有编译器关于保留循环的警告(仪器中也没有泄漏)。但是,我认为在大多数情况下,在Objective-C中避免进行这种递归块调用是明智的 - 它有点可疑。但无论如何都很有趣。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接