objective c - blocks - ¿Qué significa la palabra clave "__block"?
blocks objective c (8)
¿Qué significa exactamente la palabra clave __block
en Objective-C? Sé que te permite modificar variables dentro de bloques, pero me gustaría saber ...
- ¿Qué le dice exactamente al compilador?
- ¿Hace algo más?
- Si eso es todo lo que hace, ¿por qué es necesario en primer lugar?
- ¿Está en los documentos en alguna parte? (No puedo encontrarlo).
@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 debe administrarse de manera diferente.
...
Sin embargo, para las variables __block, el bloque no se conserva. Depende de usted retener y liberar, según sea necesario.
...
En cuanto a los casos de uso, encontrará que __block
se usa a veces para evitar los ciclos de retención ya que no retiene el argumento. Un ejemplo común es usar uno mismo.
//Now using myself inside a block will not
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
De la especificación de lenguaje de bloque :
Además del nuevo tipo de bloque, también introducimos un nuevo calificador de almacenamiento, __block, para las variables locales. [testme: una declaración de __block dentro de un bloque literal] El calificador de almacenamiento __block es mutuamente exclusivo de los calificadores de almacenamiento local existentes auto, register, y static. [testme] Las variables calificadas por __block actúan como si estuvieran en el almacenamiento asignado y este almacenamiento es Se recupera automáticamente después del último uso de dicha variable. Una implementación puede elegir una optimización donde el almacenamiento es inicialmente automático y solo se "traslada" al almacenamiento asignado (almacenamiento dinámico) en una Copia de Bloque de un Bloque de referencia. Tales variables pueden ser mutadas como lo son las variables normales.
En el caso de que una variable __block sea un bloque, se debe asumir que la variable __block reside en el almacenamiento asignado y, como tal, se supone que hace referencia a un bloque que también está en el almacenamiento asignado (que es el resultado de una operación Block_copy). A pesar de esto, no hay ninguna disposición para hacer una Block_copy o Block_release si una implementación proporciona un almacenamiento automático inicial para los bloques. Esto se debe a la condición de carrera inherente de varios subprocesos que intentan actualizar la variable compartida y la necesidad de sincronización en torno a la eliminación de los valores más antiguos y la copia de los nuevos. Dicha sincronización está fuera 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 Bloques , sección 2.3.
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 la variable de pila dentro del bloque es por defecto inmutable.
agregar __block (modificador de almacenamiento) antes de su declaración lo hace mutable dentro del bloque, es decir, __block int stackVariable=1;
Le dice al compilador que cualquier variable marcada por ella 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 tanto, 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 ver un ejemplo y más información, consulte __block Storage Type 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, tanto localCounter
como localCharacter
se modifican antes de llamar al bloque. Sin embargo, dentro del bloque, solo la modificación a localCharacter
sería visible, gracias a la palabra clave __block
. A la inversa, el bloque puede modificar el carácter localCharacter
y esta modificación es visible fuera del bloque.
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 esperar 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"
Significa que la variable de la que es un prefijo está disponible para ser utilizada dentro de un bloque.
__block
es un tipo de almacenamiento que se usa para hacer que las variables de alcance 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 más detalles, consulte Bloques Programación en iOS.
__block es un calificador de almacenamiento que se puede usar de dos maneras:
Marca que una variable vive en un almacenamiento que se comparte entre el alcance léxico de la variable original y cualquier bloque declarado dentro de ese alcance. Y clang generará una estructura para representar esta variable, y usará esta estructura por referencia (no por valor).
En MRC, __block se puede usar para evitar retener las variables de objeto que un bloque captura. Cuidado de que esto no funcione para ARC. En ARC, deberías usar __weak en su lugar.
Puede consultar la documentación de Apple para obtener información detallada.