objective c - operativos - ¿Hay un puntero SELF para bloques?
manejo de bloques libres y ocupados (4)
El siguiente código de bloque recursivo se compilará y ejecutará utilizando ARC, GC o la administración de memoria manual, sin fallar, filtrar o emitir advertencias (analizador o normal):
typedef void (^CountdownBlock)(int currentValue);
- (CountdownBlock) makeRecursiveBlock
{
CountdownBlock aBlock;
__block __unsafe_unretained CountdownBlock aBlock_recursive;
aBlock_recursive = aBlock = [^(int currentValue)
{
if(currentValue >= 0)
{
NSLog(@"Current value = %d", currentValue);
aBlock_recursive(currentValue-1);
}
} copy];
#if !__has_feature(objc_arc)
[aBlock autorelease];
#endif
return aBlock;
}
- (void) callRecursiveBlock
{
CountdownBlock aBlock = [self makeRecursiveBlock];
// You don''t need to dispatch; I''m doing this to demonstrate
// calling from beyond the current autorelease pool.
dispatch_async(dispatch_get_main_queue(), ^
{
aBlock(10);
});
}
Consideraciones importantes:
- Debe copiar el bloque en el montón manualmente o, de lo contrario, intentará acceder a una pila inexistente cuando lo llame desde otro contexto (ARC generalmente lo hace por usted, pero no en todos los casos. Es mejor jugar de forma segura).
- Necesita DOS referencias: una para mantener la fuerte referencia al bloque y otra para mantener una referencia débil para que el bloque recursivo llame (técnicamente, esto solo es necesario para el ARC).
- Debe usar el calificador __block para que el bloque no capture el valor aún no asignado de la referencia del bloque.
- Si está realizando la gestión manual de la memoria, deberá autoeleccionar el bloque copiado.
Me gustaría recursivamente llamar a un bloque desde dentro de sí mismo. En un objeto obj-c, podemos usar "self", ¿hay algo como esto para referirse a una instancia de bloque desde dentro de sí?
No hay un self
para los bloques (todavía). Puedes construir uno como este (asumiendo ARC):
__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
// Use blockSelf here
} copy];
blockSelf = block;
// Use block here
El __block
es necesario para que podamos establecer blockSelf
en el bloque después de crear el bloque. Se necesita __weak
porque, de lo contrario, el bloque mantendría una fuerte referencia a sí mismo, lo que provocaría un fuerte ciclo de referencia y, por lo tanto, una pérdida de memoria. La copy
es necesaria para asegurarse de que el bloque se copia al montón. Eso puede ser innecesario con las nuevas versiones del compilador, pero no hará ningún daño.
Tienes que declarar la variable de bloque como __block
:
typedef void (^MyBlock)(id);
__block MyBlock block = ^(id param) {
NSLog(@"%@", param);
block(param);
};
Historia divertida Los bloques en realidad son objetos Objective-C. Dicho esto, no hay una API expuesta para obtener el puntero a self
de los bloques.
Sin embargo, si declara bloques antes de usarlos, puede usarlos recursivamente. En un entorno sin recolección de basura, harías algo como esto:
__weak __block int (^block_self)(int);
int (^fibonacci)(int) = [^(int n) {
if (n < 2) { return 1; }
return block_self(n - 1) + block_self(n - 2);
} copy];
block_self = fibonacci;
Es necesario aplicar el modificador __block
a block_self
, porque de lo contrario, la referencia block_self
dentro de fibonacci
se referiría a él antes de que se asigne (bloquear su programa en la primera llamada recursiva). La __weak
es asegurar que el bloque no capture una referencia fuerte a sí mismo, lo que causaría una pérdida de memoria.