objective c - Usando__block y__weak
objective-c objective-c-blocks (2)
He leído este hilo: ¿Qué significa la palabra clave "__block"? que discute para qué se usa __block
pero estoy confundido acerca de una de las respuestas. Dice que __block
se usa para evitar los ciclos de retención, pero los comentarios debajo me dejan inseguro.
Lo estoy usando algo como esto:
self.someProperty = x; //where x is some object (id)
__block __weak VP_User *this = self;
//begin a callback-style block
this.someProperty = nil;
¿Necesito usar tanto __block
y __weak
? ¿Algún problema evidente con este aspecto?
Debería usar __block
si desea cambiar el valor de la variable en el bloque.
p.ej:
__block BOOL result = NO;
dispatch_sync(dispatch_get_main_queue(), ^{
...
result = YES;
...
});
Debes usar __weak
si quieres evitar los ciclos de retención.
p.ej:
__weak typeof(self) wself = self;
self.foobarCompletion = ^{
...
wself.foo = YES;
...
};
Puedes combinarlos si es necesario.
__block
es un calificador de almacenamiento. Especifica que la variable debe ser capturada directamente por el bloque en lugar de copiarla. Esto es útil en caso de que necesite modificar la variable original, como en el siguiente ejemplo
__block NSString *aString = @"Hey!";
void(^aBlock)() = ^{ aString = @"Hello!" }; // without __block you couldn''t modify aString
NSLog(@"%@", aString); // Hey!
aBlock();
NSLog(@"%@", aString); // Hello!
En ARC, esto hace que la variable se retenga automáticamente, de modo que se pueda hacer referencia de forma segura dentro de la implementación del bloque. En el ejemplo anterior, aString
se le envía un mensaje de retain
cuando se captura en el contexto del bloque.
No es que esto no sea cierto en MRC (Recuento manual de referencias) donde se hace referencia a la variable sin ser retenida.
__weak
como __weak
hace que la variable no se retenga, por lo que el bloque se refiere directamente a ella pero sin retenerla. Esto es potencialmente peligroso ya que en caso de que el bloque tenga una vida más larga que la variable, ya que se referirá a la memoria de basura (y es probable que se bloquee).
Aquí está el párrafo relevante del documento de Clang :
En los lenguajes Objective-C y Objective-C ++, permitimos el especificador
__block
para__block
variables__block
del tipo de objeto. [...] Este calificador hace que estas variables se mantengan sin retener los mensajes que se envían. A sabiendas, esto conduce a punteros colgantes si el Bloque (o una copia) sobrevive a la vida útil de este objeto.
Finalmente, la afirmación de que __block
se puede usar para evitar ciclos de referencia fuertes (también conocidos como ciclos de retención) es totalmente errónea en un contexto ARC. Debido al hecho de que en ARC __block
hace que la variable sea fuertemente referenciada, en realidad es más probable que la cause.
Por ejemplo, en MRC este código rompe un ciclo de retención
__block typeof(self) blockSelf = self; //this would retain self in ARC!
[self methodThatTakesABlock:^ {
[blockSelf doSomething];
}];
mientras que para lograr el mismo resultado en ARC, normalmente lo haces
__weak typeof(self) weakSelf = self;
[self methodThatTakesABlock:^ {
[weakSelf doSomething];
}];