2011-01-28 12 views

Respuesta

27

¡Historia divertida! Los bloques en realidad son objetos Objective-C. Dicho esto, no hay API expuesta para obtener el puntero self de bloques.

Sin embargo, si declara bloques antes de usarlos, puede usarlos recursivamente. En un entorno no recolección de basura, que haría 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 necesaria aplicar el modificador __block a block_self, porque de lo contrario, la referencia block_self dentro fibonacci se referiría a ella antes de que está asignado (bloqueando su programa en la primera llamada recursiva). El __weak es para garantizar que el bloque no capture una referencia fuerte a sí mismo, lo que causaría una pérdida de memoria.

+1

Excelente respuesta. el modificador de bloque __ debería haberme tomado un tiempo para resolverlo por mi cuenta. ¡Gracias una tonelada! ¡Rep para ti! –

+3

Si pasa a una función que terminará copiándola (como 'dispatch_async()'), también debe copiar el bloque en el alcance de la tarea. –

+2

Este código tendrá fugas, incluso si tiene una condición de terminación. Agregue una variable de cuenta regresiva y ejecútela a través del inspector de fugas de Instruments para ver.Además, el compilador emitirá la siguiente advertencia si tiene activadas las advertencias correctas: "Capturar 'myBlock' en este bloque probablemente lleve a un ciclo de retención" – Karl

6

Usted tiene que declarar la variable de bloque como __block:

typedef void (^MyBlock)(id); 

__block MyBlock block = ^(id param) { 
    NSLog(@"%@", param); 
    block(param); 
}; 
+0

@Joe Blow ['__block' específicamente es una palabra clave] (http://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#// apple_ref/doc/uid/TP40007502-CH6-SW6). –

+0

Este código también se fugará por la misma razón que el código de zneak. – Karl

14

El siguiente bloque de código recursivo compilar y ejecutar utilizando ARC, GC, o la gestión de memoria manual, sin que se caiga, fugas, o la emisión de advertencias (analizador o regular):

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:

  • Usted 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 hace esto por usted, pero no en todos los casos). Es mejor ir a lo seguro).
  • Necesita DOS referencias: una para mantener la referencia fuerte al bloque y otra para mantener una referencia débil para el bloque recursivo para llamar (técnicamente, esto solo es necesario para ARC).
  • Debe usar el calificador __block para que el bloque no capture el valor aún no asignado de la referencia de bloque.
  • Si está realizando la administración manual de la memoria, deberá liberar automáticamente el bloque copiado.
+0

Esto no funcionaría en ARC, pero ¿qué pasa con '' 'copy] liberación automática]' '' en lugar de solo copiar, por lo que no tiene que llamar a la versión dentro del bloque? Para ARC, tal vez haga '' 'aBlock = nil''' después de llamar' '' aBlock (10); '' '? –

+0

En realidad, al volver a visitar esta solución, resulta que mantener la referencia dentro del bloque provocaría una fuga si no se llamaba al bloque. He actualizado la respuesta para que funcione correctamente, ya sea que la llame o no. – Karl

+0

Tengo un problema que agrega una capa de complejidad a esto, pero no estoy del todo seguro de que mi problema se pueda resolver. Me gustaría poder volver a intentar llamadas API a través de AFNetworking. Creo que este pastebin simplifica e ilustra el problema. http://pastebin.com/xRPJuckZ El bloque recursivo no seguro que no se ha conservado se llama desde dentro de otro bloque dentro de sí mismo y llamarlo allí genera una excepción de acceso incorrecto. ¿Tendría alguna idea de cómo resolver esto? –

3

No hay self para bloques (todavía). Usted puede construir uno como este (suponiendo ARC):

__block void (__weak ^blockSelf)(void); 
void (^block)(void) = [^{ 
     // Use blockSelf here 
} copy]; 
blockSelf = block; 
    // Use block here 

se necesita la __block para que podamos establecer blockSelf al bloque después de crear el bloque. El __weak es necesario porque, de lo contrario, el bloque tendrí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. El copy es necesario para asegurarse de que el bloque se copie en el montón. Eso puede ser innecesario con versiones de compilador más nuevas, pero no hará ningún daño.

Cuestiones relacionadas