ARC, bloques y ciclos de retención

Trabajando en un proyecto iOS que se dirige a 4.0 y 5.0, usando ARC.

Se está ejecutando un problema relacionado con bloques, ARC y haciendo reference a un object desde fuera del bloque. Aquí hay un código:

__block AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setCompletionBlock:^ { if ([operation isCancelled]) { return; } ... do stuff ... operation = nil; }]; 

En este caso, el comstackdor da una advertencia de que usar 'operación' en el bloque llevará a un ciclo de retención. Bajo ARC, __bloque ahora retiene la variable.

Si agrego __unsafe_unretained, el comstackdor libera el object inmediatamente, así que obviamente eso no funcionará.

Estoy apuntando a 4.0, así que no puedo usar __dilugio.

Intenté hacer algo como esto:

 AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; __block __unsafe_unretained AFHTTPRequestOperation *weakOperation = operation; 

pero mientras la operación débil no es nula, ninguna de sus properties se llena cuando se encuentra dentro del bloque.

¿Cuál es la mejor manera de manejar esta situación teniendo en count las limitaciones del proyecto enumeradas anteriormente?

Si se asumen garantías de progreso, un ciclo de retención podría ser exactamente lo que desea. Rompe explícitamente el ciclo de retención al final del bloque, por lo que no es un ciclo de retención permanente: cuando se llama al bloque, el ciclo se interrumpe.

Sin embargo, si tiene algo más que mantenga la operación alnetworkingedor, puede almacenar una reference en una variable no __unsafe_unretained __weak o no __unsafe_unretained y luego usarla desde su bloque. No hay necesidad de __block califique la variable a less que, por alguna razón, necesite cambiar la vinculación de la variable durante el locking; ya que no tiene un ciclo de retención para romper más, no debería asignar nada a la variable débil.

Este parece ser el problema descrito por Conrad Stoll en Bloques, operaciones y Retain Cycles , pero su resumen omite algunos puntos importantes:

  • __block parece la forma recomendada por Apple de evitar una fuerte reference a las variables capturadas en el modo MRC, pero es completamente innecesario en el modo ARC. En este caso, es completamente innecesario en el modo ARC; también es innecesario en modo MRC, aunque la solución de peso más ligero es mucho más detallada: void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; } void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • En el modo ARC, necesita una reference sólida (para poder agregarla a la queue) y una reference débil / no segura

La solución más simple se ve así:

 AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; AFHTTPRequestOperation * __unsafe_unretained unretainedOperation = operation; [operation setCompletionBlock:^ { if ([unretainedOperation isCancelled]) { return; } ... do stuff ... }]; 

Incluso si rompe el ciclo de reference, no hay ninguna razón para que el Bloque retenga la AFHTTPRequestOperation en primer lugar (suponiendo que la operación se mantiene viva hasta que se completa el manejador de finalización, lo cual no siempre está garantizado, pero generalmente es verdadero y asumido por ARC si se hace reference al uso de self más arriba de la stack de llamadas).

La mejor solución parece ser actualizar a la última AFNetworking , que pasa la operación al bloque como argumento.