阻止循环,打破保留周期(Block recursion and breaking retain cy

2019-08-16 22:23发布

为了更好地说明这个问题,考虑块递归以下简化形式:

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

的XCode(启用ARC-)警告说,“ 捕获‘下一步’强烈该块很可能会导致保留周期 ”。

同意。

问题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:你会是一个更好的实现块递归的?

谢谢。

Answer 1:

为了完成保留周期,免费递归块的执行,就需要使用两个块参照 - 一个弱和强一个。 因此,对于你的情况,这是代码可能是什么样子:

__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),以及外部环境捕获强引用(下一个),以保持块周围。 两个引用指向相同的块。

见https://stackoverflow.com/a/19905407/1956124对于这个模式中,也使用块递归的另一个例子。 此外,在下面的文章的评论部分的讨论与此相关的还有: http://ddeville.me/2011/10/recursive-blocks-objc/



Answer 2:

我认为@newacct约为@马特威尔丁的解决方案正确; 它似乎没有什么会产生强烈的裁判在这种情况下,下一个和运行时会导致运行时异常(至少它为我做的)。

我不知道它是多么常见的发现递归调用块在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

这将运行对我罚款,没有编译器警告有关保留周期(在仪表没有泄漏)。 不过,我想我可能会避开这样做在大多数情况下objc这个(递归块调用)的 - 它的臭。 但在任何情况下有趣。



文章来源: Block recursion and breaking retain cycle