2011-10-13 42 views
28

Trabajando en un proyecto iOS que apunta a 4.0 y 5.0, usando ARC.Ciclo de ARC, Bloques y retención

Al encontrarse con un problema relacionado con bloques, ARC y hacer referencia a un objeto desde fuera del bloque. Aquí hay algo de 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 el uso de 'operación' en el bloque va a conducir a un ciclo de retener. 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, por lo que no puedo usar __weak.

He intentado hacer algo como esto:

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

pero mientras weakOperation no es nula, ninguna de sus propiedades están llenando a medida que el interior del bloque.

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

Respuesta

23

Asumiendo garantías de progreso, un ciclo de retención puede ser exactamente lo que usted 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 es necesario __block -calificar 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.

+1

Tengo el problema de "no retener el ciclo" en mi mente, ni siquiera pensé en eso de la manera que me describiste. Duh. Siguiente pregunta: ¿alguna forma de silenciar la advertencia del compilador? Me volverá loco. – Hunter

+1

Ver ["Controlar diagnósticos a través de Pragmas"] (http://clang.llvm.org/docs/UsersManual.html#diagnostics_pragmas) en el manual del usuario de Clang. Solo tendrá que averiguar qué indicador de advertencia ignorar. –

+4

Es '#pragma clang diagnóstico ignorado" -Warc-retain-cycles "', por el. –

1

Este parece ser el problema descrito por Conrad Stoll en Blocks, Operations, and Retain Cycles, pero su valoración crítica pierde algunos puntos importantes:

  • __block se parece a la forma recomendada por Apple de evitar una fuerte referencia a variables capturados en MRC modo pero es completamente innecesario en el modo ARC. En este caso, es completamente innecesario en el modo ARC; tampoco es necesario en el modo MRC pesar de que la solución más ligera de peso es mucho más detallado: void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • En el modo ARC, se necesita tanto una referencia fuerte (lo que puede añadir a la cola) y una débil referencia/unsafe_unretained

la solución más simple es el siguiente:

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

[operation setCompletionBlock:^ { 
    if ([unretainedOperation isCancelled]) { 
    return; 
    } 
    ... do stuff ... 
}]; 

Incluso si se rompe el ciclo de referencia, no hay razón para el bloqueo para retener el AFHTTPRequestOperation en el primer lugar (suponiendo que la operación mantiene propia vida hasta t El controlador de finalización completa, que no siempre es con garantizado, pero generalmente es verdadero y asumido por ARC si se lo utiliza usando self en la parte superior de la pila de llamadas.

Parece que la mejor corrección es actualizar al latest AFNetworking, que pasa la operación al bloque como un argumento.

+0

__block no es completamente innecesario. Por defecto, todas las variables, que fueron retenidas por bloque, estarán dentro de él, por lo que no puede cambiar sus valores. Aquí __block viene al rescate. –

Cuestiones relacionadas