ios - Cómo arreglar "fuertemente" el bloqueo de ''bloque'' en este bloque es probable que conduzca a un ciclo de retención "
objective-c asynchronous (1)
Estoy trabajando en este código, que realiza una larga operación asincrónica en la red y cuando termina desencadena un bloque de finalización donde se ejecuta alguna prueba y si una variable obtiene un cierto valor, otra operación prolongada debería comenzar inmediatamente:
-(void) performOperation
{
void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){
int variable=0;
// Do completion operation A
//...
//...
// Do completion operation B
//Get the variable value
if(variable>0){
[self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
}
};
//Perform the lenhgty operation with the above completionBlock
[self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
}
-(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock
{
//Do some lengthy asynchronous stuff
}
Con este código recibo esta advertencia del compilador:
WARNING: Block pointer variable ''completionBlock'' is uninitialized when caputerd by the block
Cambié:
void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)
en:
__block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)
pero recibo esta otra advertencia:
WARNING 2: Capturing ''completionBlock'' strongly in this block is likely to lead to a retain cycle
¿Cómo puedo arreglar esto?
Gracias
Nicola
ADVERTENCIA: La variable de puntero de bloque ''completionBlock'' no se inicializa cuando es capturada por el bloque
Esto sucede porque las variables de bloque inicializadas en un bloque recursivo necesitan __block
storage.
- Las variables dentro de un bloque se copian a menos que se declaren con
__block
, en cuyo caso se pasan como referencia. - Cuando se asigna un bloque recursivo a una variable de bloque, la creación ocurre antes de la asignación, y dicha creación activa una copia variable. Dado que la variable aún no se ha asignado, la variable copiada tendrá un valor incorrecto y producirá un bloqueo cuando se ejecute el bloque.
- Pero si agregamos
__block
, el bloque se creará con una referencia a la variable. Luego, la variable se inicializará en el bloque creado y el bloque estará listo para usar.
ADVERTENCIA: capturar ''completionBlock'' con fuerza en este bloque es probable que conduzca a un ciclo de retención
Esto sucede porque una variable de bloque es una referencia fuerte al bloque, y el bloque mismo hace referencia a la variable (porque como vimos antes, la variable tiene un __block
por lo que se hace referencia en su lugar se copia).
Así que necesitamos
- Una referencia débil a la variable fuerte dentro del bloque.
- Y una fuerte referencia externa para evitar que el bloque sea desasignado durante el alcance del método en el que se crea.
void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request); void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request); weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){ [self lengthyAsyncMethod:weakCompletionBlock]; };
El nombre doLengthyAsynchronousOperationWithCompletionBlock
sugiere que el método puede sobrevivir al alcance del método donde se crea el bloque. Dado que el compilador no copia un bloque pasado como argumento, es responsabilidad de este método copiar este bloque. Si estamos utilizando este bloque con un código de bloqueo (por ejemplo: dispatch_async()
), esto sucede automáticamente.
Si hubiéramos estado asignando este bloque a una variable de instancia, necesitaríamos @property(copy)
y una referencia débil a self dentro del bloque, pero este no es el caso, así que solo usamos self.