objective-c objective-c-blocks weak-references

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]; }];