块和保留循环

6
有一个小问题:为什么Xcode会抱怨Listing 1会导致保留环,而在Listing 2中却没有?在两种情况下,_clients都是一个int实例变量。在Listing 2中,它在init方法中被赋值为0

背景信息:我想在块中执行循环,只要至少有一个客户端请求从iPhone加速计更新,我就将其发布到redis频道。如果没有更多的客户端,循环将退出并停止发布加速计数据。

Listing 2来自我编写的一个小测试应用程序,以验证我的想法是否可行。 Listing 1在真实项目中实现。

Listing 1

- (id)init {
  self = [super init];

  if (self) {
    _clients = 0;

    /**
     * The callback being executed
     */
    _callback = ^ {
      while (_clients > 0) { // Capturing 'self' strongly in this block is likely to lead to a retain cycle
        NSLog(@"Publish accelerometer data to redis (connected clients: %d)", _clients);
      }
    };
  }

  return self;
}

清单 2

- (void)touchedConnectButton:(id)sender {
  _clients += 1;

  dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_async(concurrentQueue, ^() {
    while(_clients > 0) {
      NSLog(@"Connected clients: %d", _clients);
    }
  });
}
2个回答

7
在这两个列表中,您都在引用实例变量,因此隐式捕获了self。一个强大的self。
这导致了您问题的第一个解决方案:
int clients = _clients;
// use `clients` instead of `_clients` in your blocks

或者,您可以使用一个弱引用:

id __weak weakself = self;
// use `weakself->_clients` in your blocks

您在列表1中出现错误的原因是因为该块捕获了self,并且该块存储在相同的self实例变量中,导致保留循环。上述两个解决方案都将解决此问题。

请注意,我在这里使用了 id 作为 weakself,但你应该使用适当的类型。如果你使用 Objective-C++,你可以将本地副本 clients 声明为 auto clients,推断变量类型。 - fabrice truillot de chambrier
Listing#2 是一个保留循环吗? - Just a coder

5
在代码清单1中存在一个保留循环,因为在代码清单1中self保留了ivar _callback,在其中访问了另一个ivar _clients,并且由于_clients是原始变量,所以块保留了self以访问它!
在代码清单2中,块被队列保留而不是self。

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