¿Qué significa la palabra clave "__block"?

446

¿Qué significa exactamente la __blockpalabra clave en Objective-C? Sé que te permite modificar variables dentro de bloques, pero me gustaría saber ...

  1. ¿Qué le dice exactamente al compilador?
  2. ¿Hace algo más?
  3. Si eso es todo lo que hace, ¿por qué es necesario en primer lugar?
  4. ¿Está en los documentos en alguna parte? (No puedo encontrarlo).
mjisrawi
fuente
3
marque aquí , y la sección "Bloques y variables".
1
@Code Monkey: estaba preguntando específicamente sobre la palabra clave, no la sintaxis en general. Así que no pienses que es realmente un duplicado.
mjisrawi
3
@Code Monkey: No, esto no es un duplicado. La pregunta que mencionas no habla __blocken absoluto.
DarkDust
3
Y si alguien se pregunta cómo los Objective-C __blockdeberían traducirse a Swift: "Los cierres [en Swift] tienen una semántica de captura similar a los bloques [en Objective-C] pero difieren en una forma clave: las variables son mutables en lugar de copiadas. En otras palabras, el comportamiento de __block en Objective-C es el comportamiento predeterminado para las variables en Swift ". Del libro de Apple: Uso de Swift con Cocoa y Objective-C (Swift 2.2).
Jari Keinänen

Respuestas:

543

Le dice al compilador que cualquier variable marcada por él debe tratarse de una manera especial cuando se usa dentro de un bloque. Normalmente, las variables y sus contenidos que también se usan en bloques se copian, por lo que cualquier modificación realizada a estas variables no se muestra fuera del bloque. Cuando están marcados con __block, las modificaciones realizadas dentro del bloque también son visibles fuera de él.

Para obtener un ejemplo y más información, consulte El tipo de almacenamiento __block en los Temas de programación de bloques de Apple .

El ejemplo importante es este:

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;

{
    NSInteger localCounter = 42;
    __block char localCharacter;

    void (^aBlock)(void) = ^(void) {
        ++CounterGlobal;
        ++CounterStatic;
        CounterGlobal = localCounter; // localCounter fixed at block creation
        localCharacter = 'a'; // sets localCharacter in enclosing scope
    };

    ++localCounter; // unseen by the block
    localCharacter = 'b';

    aBlock(); // execute the block
    // localCharacter now 'a'
}

En este ejemplo, ambos localCountery localCharacterse modifican antes de que se llame al bloque. Sin embargo, dentro del bloque, solo la modificación a localCharactersería visible, gracias a la __blockpalabra clave. Por el contrario, el bloque puede modificarse localCharactery esta modificación es visible fuera del bloque.

Polvo oscuro
fuente
8
Excelente, explicación concisa, y un ejemplo muy útil. ¡Gracias!
Evan Stone
1
¿Cómo modifica aBlock localCounter? Solo parece modificar CounterGlobal. Gracias
CommaToast
8
No modifica localCounter, pero modifica localCharacter. Además, preste atención al valor que localCountertiene en el bloque: es 42, a pesar de que la variable aumenta antes de que se llame al bloque, pero después de que se creó el bloque (fue entonces cuando el valor se "capturó").
DarkDust
1
Sin embargo, esa es una explicación útil. ¿Puede explicar lo que parecen ser declaraciones contradictorias en su explicación? Usted dice anteriormente que "aBlock modifica ... localCounter" y luego, en los comentarios, dice "[aBlock] NO modifica localCounter". Cual es Si no está "modificado", ¿debería editarse su respuesta?
Praxiteles
2
En general, los vars sin __bloque serían capturados por valor y empaquetados en el "entorno" del bloque, cuando se crea el bloque. Pero los __bloques de bloque no se capturarán, siempre que se usen dentro o fuera de un bloque, se hace referencia a ellos tal como están.
jchnxu
27

@bbum cubre bloques en profundidad en una publicación de blog y toca el tipo de almacenamiento __block.

__block es un tipo de almacenamiento distinto

Al igual que estático, automático y volátil, __block es un tipo de almacenamiento. Le dice al compilador que el almacenamiento de la variable se administrará de manera diferente.

...

Sin embargo, para las variables __block, el bloque no se retiene. Depende de usted retener y liberar, según sea necesario.
...

En cuanto a los casos de uso que encontrará, a __blockveces se usa para evitar retener ciclos ya que no retiene el argumento. Un ejemplo común es el uso de uno mismo.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
Joe
fuente
Consulte esta publicación para obtener más información sobre el problema del ciclo de retención: benscheirman.com/2012/01/… . ¿Sería __weaksuficiente en este caso específico también? Es un poco más claro quizás ...
Hari Karam Singh
17
Finalmente, la afirmación de que __block puede usarse para evitar ciclos de referencia fuertes (también conocidos como ciclos de retención) es simplemente errónea en un contexto ARC. Debido al hecho de que en ARC __block hace que la variable tenga una fuerte referencia, en realidad es más probable que las cause. stackoverflow.com/a/19228179/189006
Krishnan
10

Normalmente, cuando no usa __block, el bloque copiará (retendrá) la variable, por lo que incluso si modifica la variable, el bloque tiene acceso al objeto anterior.

NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"

En estos 2 casos necesitas __block:

1.Si desea modificar la variable dentro del bloque y espera que sea visible afuera:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"

2.Si desea modificar la variable después de haber declarado el bloque y espera que el bloque vea el cambio:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"
Hamid Vakilian
fuente
8

__block es un calificador de almacenamiento que se puede usar de dos maneras:

  1. Marca que una variable vive en un almacenamiento que se comparte entre el alcance léxico de la variable original y los bloques declarados dentro de ese alcance. Y clang generará una estructura para representar esta variable, y usará esta estructura por referencia (no por valor).

  2. En MRC, __block puede usarse para evitar retener variables de objeto que un bloque captura. Tenga cuidado de que esto no funcione para ARC. En ARC, debes usar __weak en su lugar.

Puede consultar apple doc para obtener información detallada.

Mindy
fuente
6

__blockes un tipo de almacenamiento que se utiliza para hacer que las variables de ámbito sean mutables, más francamente si declara una variable con este especificador, su referencia se pasará a los bloques, no a una copia de solo lectura. Para obtener más detalles, consulte la Programación de bloques en iOS

mithilesh
fuente
2

Espero que esto te ayudará

supongamos que tenemos un código como:

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

dará un error como "la variable no es asignable" porque las variables de la pila dentro del bloque son por defecto inmutables.

agregar __block (modificador de almacenamiento) antes de la declaración lo hace mutable dentro del bloque, es decir __block int stackVariable=1;

Anurag Bhakuni
fuente
1

De la especificación del idioma del bloque :

Además del nuevo tipo de bloque, también presentamos un nuevo calificador de almacenamiento, __block, para variables locales. [testme: una declaración de __block dentro de un literal de bloque] El calificador de almacenamiento __block es mutuamente exclusivo para los calificadores de almacenamiento local existentes auto, register y static. [testme] Las variables calificadas por __block actúan como si estuvieran en almacenamiento asignado y este almacenamiento es recuperado automáticamente después del último uso de dicha variable. Una implementación puede elegir una optimización en la que el almacenamiento es inicialmente automático y solo se "mueve" al almacenamiento asignado (montón) después de un Block_copy de un Bloque de referencia. Dichas variables pueden mutarse como lo son las variables normales.

En el caso de que una variable __block sea un Block, se debe suponer que la variable __block reside en el almacenamiento asignado y, como tal, se supone que hace referencia a un Block que también está en el almacenamiento asignado (que es el resultado de una operación Block_copy). A pesar de esto, no existe ninguna disposición para hacer una Block_copy o una Block_release si una implementación proporciona almacenamiento automático inicial para Blocks. Esto se debe a la condición de carrera inherente de potencialmente varios subprocesos que intentan actualizar la variable compartida y la necesidad de sincronización para deshacerse de los valores más antiguos y copiar los nuevos. Dicha sincronización está más allá del alcance de esta especificación de lenguaje.

Para obtener detalles sobre lo que debe compilar una variable __block, consulte la Especificación de implementación de bloque , sección 2.3.

Martin Gordon
fuente
Sus enlaces están muertos
Ben Leggiero
Esto no es realmente una respuesta y podría desarrollarse o eliminarse. ¡Gracias!
Dan Rosenstark
0

Significa que la variable a la que es un prefijo está disponible para usarse dentro de un bloque.

Rich Allen
fuente