Block recursion and breaking retain cycle

后端 未结 2 1856
清酒与你
清酒与你 2021-02-13 00:03

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

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
           


        
相关标签:
2条回答
  • 2021-02-13 00:26

    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/

    0 讨论(0)
  • 2021-02-13 00:30

    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.

    0 讨论(0)
提交回复
热议问题