¿Cómo captura un bloque las variables fuera de su scope adjunto?

Sé que un Bloque Objective-C puede capturar y establecer el valor de las variables fuera de su scope adjunto. ¿Como hace eso?

En realidad, es bastante sencillo y se describe en la Especificación de implementación de bloques de Clang, en la sección “Variables importadas” .

Cuando el comstackdor encuentra un bloque como:

^{ if( numBalloons > numClowns) abort(); } 

crea una estructura literal que incluye, entre otras cosas, dos elementos que son importantes aquí. Hay un puntero de función para el código ejecutable en el bloque y un campo const para cada variable a la que se hace referencia dentro del bloque. Algo como esto:

 struct __block_literal_1 { /* other fields */ void (*invoke)(struct __block_literal_1 *); /* ... */ const int numBalloons; const int numClowns; }; 

Observe que la función de invoke tomará un puntero a una estructura del tipo que se está definiendo aquí; es decir, el bloque se transfiere al ejecutar su código. Por lo tanto, el código obtiene acceso a los miembros de la estructura.

Justo después de la statement, el comstackdor crea una definición del bloque, que simplemente usa las variables referenciadas para inicializar los campos correctos en la struct :

 struct __block_literal_1 __block_literal_1 = { /* Other fields */ __block_invoke_2, /* This function was also created by the compiler. */ /* ... */ numBalloons, /* These two are the exact same variables as */ numClowns /* those referred to in the Block literal that you wrote. * }; 

Luego, dentro de la función de invoke , las referencias a las variables capturadas se hacen como cualquier otro miembro de una estructura, the_block->numBalloons .

La situación para las variables de tipo de objeto es un poco más complicada, pero se aplica el mismo principio.

Dentro del cuerpo de código del objeto de bloque, las variables se pueden tratar de cinco maneras diferentes.

Puede hacer referencia a tres tipos de variables estándar, tal como lo haría desde una función:

  • Variables globales, incluidos locales estáticos
  • Funciones globales (que técnicamente no son variables)
  • Variables locales y parámetros de un scope adjunto

Los bloques también admiten otros dos tipos de variables:

  1. En el nivel de función son __block variables. Estos son mutables dentro del bloque (y el scope adjunto) y se conservan si se copia cualquier bloque de referencia en el montón.

  2. const importaciones.

Finalmente, dentro de una implementación de método, los bloques pueden hacer referencia a las variables de instancia de Objective-C -ver Object y Block Variables.

Las siguientes reglas se aplican a las variables utilizadas dentro de un bloque:

  1. Las variables globales son accesibles, incluidas las variables estáticas que existen dentro del scope léxico adjunto.

  2. Los parámetros pasados ​​al bloque son accesibles (como los parámetros de una función).

  3. Las variables de stack (no estáticas) locales al scope léxico adjunto se capturan como variables const .

    Sus valores se toman en el punto de la expresión de bloque dentro del progtwig. En bloques nesteds, el valor se captura desde el scope circundante más cercano.

  4. Las variables locales al scope léxico adjunto declarado con el modificador de almacenamiento __block se proporcionan por referencia y son mutables.

    Cualquier cambio se refleja en el ámbito léxico que lo incluye, incluyendo cualquier otro bloque definido dentro del mismo scope léxico. Estos se discuten con más detalle en El tipo de almacenamiento __block.

  5. Variables locales declaradas dentro del scope léxico del bloque, que se comportan exactamente como variables locales en una función.

Cada invocación del bloque proporciona una nueva copia de esa variable. Estas variables pueden, a su vez, ser utilizadas como variables const o by-reference en bloques encerrados dentro del bloque.

De aquí:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html

Básicamente, el bloque “objeto” contiene una variable dentro del objeto bloque (como una “variable de instancia” del objeto bloque) para cada variable local capturada. (La respuesta de Josh Caswell proporciona más detalles sobre cómo se implementa). Cuando se crea el bloque, el valor de cada variable local capturada en ese momento se copia en la variable correspondiente dentro del bloque. Siempre que la variable se use dentro del bloque, usa esta variable dentro del bloque.