简体   繁体   English

阻止递归并打破保留周期

[英]Block recursion and breaking retain cycle

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 ". XCode(启用ARC)警告“ 在此块中强烈捕获'下一个'可能会导致保留周期 ”。

Agreed. 同意。

Question 1 : Would the retain cycle be successfully broken by setting the block itself to nil , in this fashion: 问题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);

(Note: you'd still get the same warning, but perhaps it is unwarranted) (注意:你仍然会得到同样的警告,但也许是没有根据的)

Question 2 : What would be a better implementation of block recursion? 问题2 :块递归的更好实现是什么?

Thanks. 谢谢。

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. 注意,块捕获弱块引用(weak_next),外部上下文捕获强引用(下一个)以保持块周围。 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. 有关此模式的另一个示例,请参阅https://stackoverflow.com/a/19905407/1956124 ,该模式也使用块递归。 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/ 此外,以下文章的评论部分中的讨论也与此相关: http//ddeville.me/2011/10/recursive-blocks-objc/

I think @newacct is correct about @Matt Wilding's solution; 我认为@newacct对@Matt Wilding的解决方案是正确的; 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. 我不知道在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. 但是,我认为在objc的大多数情况下,我可能会避免这样做(递归块调用) - 它很臭。 But interesting in any case. 但无论如何都很有趣。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM