Referencias débiles en bloques y ciclos de retención

En esta pregunta , pregunté sobre el siguiente código y los ciclos de retención:

__weak Cell *weakSelf = self; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ UIImage *image = /* render some image */ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [weakSelf setImageViewImage:image]; }]; }]; [self.renderQueue addOperation:op]; 

Todas las respuestas indican que el uso de una reference débil aquí no era necesario, ya que este código no da como resultado un ciclo de retención. Sin embargo, mientras se experimenta con un poco más de código, lo siguiente da como resultado un ciclo de retención (si no utilizo una reference débil, el controller de vista actual no se desasigna)

  //__weak ViewController *weakSelf = self; MBItem *close = [[MBItem alloc] initWithBlock:^{ [self dismissModalWithDefaultAnimation:NO]; }]; NSMutableArray *items = [[NSMutableArray alloc] initWithObjects:close, nil]; [self.childObject setItems:items]; 

¿Por qué el segundo resultado en un ciclo de retención pero no el primero?

Su código anterior crea este ciclo de retención si no utiliza __weak :

  • (NSBlockOperation *)op conserva el bloque externo
  • El bloque externo se conserva (si no estás usando __weak )
  • self conserva (NSOperationQueue *)renderQueue
  • (NSOperationQueue *)renderQueue conserva (NSBlockOperation *)op

Ninguno de los objects en ese ciclo se puede desasignar a less que uno de esos enlaces se rompa. Pero el código que nos mostró rompe el ciclo de retención. Cuando op termina de ejecutarse, renderQueue libera, rompiendo el ciclo de retención.

Sospecho que su nuevo código crea este ciclo de retención:

  • (MBItem *)close conserva el bloque
  • El bloque se conserva
  • self childObject
  • childObject retiene (NSMutableArray *)items
  • (NSMutableArray *)items retiene (MBItem *)close

Si no ocurre nada al romper uno de esos enlaces, ninguno de los objects en el ciclo puede ser desasignado. No nos ha mostrado ningún código que rompa el ciclo de retención. Si no hay ningún evento que lo rompa explícitamente (por ejemplo, al borrar childObject.items ), entonces debe usar __weak para romper el ciclo de retención.

No puedo decirte el motivo del ciclo de retención en tu segundo ejemplo, porque no conozco MBItem , pero hay dos patrones de uso diferentes con bloques.

Si espera que su bloque se ejecute en cualquier caso, puede usar el self en el bloque:

 [startSomeOperationWithCompletionBlock:^{ [self doSomeThing]; }]; 

El bloque conserva una reference a self , por lo que ese self no se desasigna antes de ejecutar el bloque. Pero después de ejecutar el bloque, esta reference (y el ciclo de retención) desaparece.

Si posiblemente desee que ese self se desasigne antes de que se ejecute el bloque, o si es posible que no se llame al bloque, entonces debe usar una reference débil y comprobar el valor dentro del bloque:

 __weak MyClass *weakSelf = self; [startSomeOperationWithCompletionBlock:^{ MyClass *strongSelf = weakSelf; if (strongSelf) { [strongSelf doSomeThing]; } }]; 

El bloque no se conserva en este caso, por lo que el self puede ser desasignado. En ese caso, weakSelf se establece en nil automáticamente. Por lo tanto, si el bloque se ejecuta finalmente, primero debe verificar si weakSelf todavía es válido. (O simplemente puede usarlo, porque enviar posts a nil es operativo).

Asignar una sólida reference strongSelf dentro del bloque evita que self desasigne a uno mismo mientras se ejecuta el bloque.