Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blocks and retain cycles

One minor question: why is Xcode complaining that listing 1 would lead to a retain cycle, while in listing 2 it does not? In both cases _clients is an int instance variable. In listing 2 it is assigned 0 in the init method.

Background info: I would like to execute the loop in the block, as long as at least one client is requesting updates from the iPhone accelerometer, which I am publishing to a redis channel. If there are no more clients left, the loop would quit and stop publishing accelerometer data.

Listing 2 comes from a small test app I wrote to verify that my idea works. Listing 1 is implemented in the real project.

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;
}

Listing 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);
    }
  });
}
like image 388
mAu Avatar asked Jul 11 '12 08:07

mAu


2 Answers

In both listings, you are referring to an instance variable, thus implicitly capturing self. A strong self.

This leads to a first solution to your problem:

int clients = _clients;
// use `clients` instead of `_clients` in your blocks

Alternatively, you can use a weak self:

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

The reason why you got the error in listing 1 is because the block captures self and the block is stored in an instance variable of the same self, leading to a retain cycle. Both solutions above will solve that issue.

like image 89
fabrice truillot de chambrier Avatar answered Sep 30 '22 17:09

fabrice truillot de chambrier


There is a retain cycle in Listing 1 because in Listing1 self is retaining the ivar _callback in which you access an other ivar, _clients, and because _clients is a primitive var the block retains self to access it !

In Listing2, the block is retained by the queue and not by self.

like image 34
iSofTom Avatar answered Sep 30 '22 16:09

iSofTom