¿Cuál es la diferencia entre una referencia __weak y una __block?

80

Estoy leyendo la documentación de Xcode y aquí hay algo que me desconcierta:

__block typeof(self) tmpSelf = self;
[self methodThatTakesABlock:^ {
    [tmpSelf doSomething];
}];

Lo siguiente se copia de la documentación:

Un bloque forma una fuerte referencia a las variables que captura. Si lo usa selfdentro de un bloque, el bloque forma una fuerte referencia al bloque self, por lo que si selftambién tiene una fuerte referencia al bloque (lo que normalmente hace), se obtiene un fuerte ciclo de referencia. Para evitar el ciclo, debe crear una __blockreferencia débil (o ) a sí mismo fuera del bloque, como en el ejemplo anterior.

No entiendo qué significa 'un débil (o __block)'?

Es

__block typeof(self) tmpSelf = self;

y

__weak typeof(self) tmpSelf = self;

exactamente lo mismo aquí?

Encontré otra pieza en el documento:

Nota: En un entorno de recolección de basura, si aplica modificadores __weaky __blocka una variable, el bloque no garantizará que se mantenga viva.

Entonces, estoy totalmente desconcertado.

HanXu
fuente

Respuestas:

109

De los documentos sobre __block

__Las variables de bloque viven en un almacenamiento que se comparte entre el alcance léxico de la variable y todos los bloques y copias de bloque declaradas o creadas dentro del alcance léxico de la variable. Por lo tanto, el almacenamiento sobrevivirá a la destrucción del marco de la pila si alguna copia de los bloques declarados dentro del marco sobrevive más allá del final del marco (por ejemplo, si se pone en cola en algún lugar para su posterior ejecución). Varios bloques en un ámbito léxico dado pueden usar simultáneamente una variable compartida.

De los documentos sobre __weak

__weak especifica una referencia que no mantiene vivo el objeto referenciado. Una referencia débil se establece en cero cuando no hay referencias fuertes al objeto.

Entonces son cosas técnicamente diferentes. __block es evitar que su variable se copie desde su alcance externo a su alcance de bloque. __weak es un puntero débil que se auto delimita.

Tenga en cuenta que dije técnicamente, porque para su caso harán (casi) lo mismo. La única diferencia es si está usando ARC o no. Si su proyecto usa ARC y es solo para iOS4.3 y superior, use __weak. Garantiza que la referencia se establezca en nil si la referencia de alcance global se publica de alguna manera. Si su proyecto no usa ARC o es para versiones anteriores del sistema operativo, use __block.

Aquí hay una diferencia sutil, asegúrese de comprenderla.

EDITAR: Otra pieza del rompecabezas es __unsafe_unretained. Este modificador es casi el mismo que __weak pero para entornos de ejecución anteriores a 4.3. SIN EMBARGO, no está establecido en cero y puede dejarlo con punteros colgantes.

Paul de Lange
fuente
1
¿Sigue siendo aplicable a iOS7 con ARC? Ejecuté un generador de perfiles y veo que mis controladores se están liberando incluso si no uso __block o __weak y hago referencia a mí mismo en un bloque.
Jay Q.13 de
1
¿Qué tal usarlos juntos? __block _weak NSString *strEg;?
CyberMew
5

En el modo de recuento de referencias manual, __block id x; tiene el efecto de no retener x. En el modo ARC, __block id x; por defecto conserva x (como todos los demás valores). Para obtener el comportamiento del modo de conteo de referencia manual en ARC, puede usar __unsafe_unretained __block id x ;. Sin embargo, como implica el nombre __unsafe_unretained, tener una variable no retenida es peligroso (porque puede colgar) y, por lo tanto, no se recomienda. Dos mejores opciones son usar __weak (si no necesita ser compatible con iOS 4 o OS X v10.6) o establecer el valor de __block en nil para romper el ciclo de retención.

documentos de Apple

Andrei Shender
fuente
0

Cuando use self en block, debe usar __weak , no __block ya que puede retener self.

En caso de que necesite un yo fuerte, puede usarlo así:

__weak typeof(self) *weakSelf = self;
[self methodThatTakesABlock:^{
    if (weakSelf) {
        __strong typeof(self) *strongSelf = weakSelf;
        [strongSelf doSomething];
    }
}];
david72
fuente