apple iphone ios ios5 automatic-ref-counting afnetworking

iphone - apple



ARC, bloques y ciclos de retenciĆ³n (2)

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

Al encontrarse con un problema relacionado con bloques, ARC y hacer referencia a un objeto 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 compilador da una advertencia de que usar ''operación'' en el bloque conducirá a un ciclo de retención. En ARC, __block ahora conserva la variable.

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

Me estoy orientando a 4.0, así que no puedo usar __weak.

Intenté hacer algo como esto:

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

pero mientras weakOperation no es nulo, ninguna de sus propiedades se llena cuando está dentro del bloque.

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


Suponiendo 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 otra cosa que mantiene la operación, puede almacenar una referencia en una variable __weak o __unsafe_unretained y luego usarla desde dentro de su bloque. No hay necesidad de " __block " la variable a menos que, por algún motivo, deba cambiar la vinculación de la variable durante el bloqueo; ya que no tiene un ciclo de retención para romper más, no debería necesitar asignar nada a la variable débil.


Este parece ser el problema descrito por Conrad Stoll en Blocks, Operations and Retain Cycles , pero su descripción omite algunos puntos importantes:

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

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 referencia, no hay ninguna razón para que el Bloque retenga el AFHTTPRequestOperation en primer lugar (suponiendo que la operación se mantenga activa hasta que finalice el controlador de finalización, lo que no siempre está garantizado pero generalmente es verdad y asumido por ARC si se hace referencia a usar self más arriba en la pila de llamadas).

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