Creando el bucle usando GCD

Así que esto es lo que tengo:

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), ^{
                bool ready = some_function();
                if( ready ) {                    
                   do_smth_here()
                } else {
                   //invoke this block one more time after 0.1 sec
                }
            });

El problema es ¿cómo puedo obtener la referencia al bloque actual?

preguntado el 12 de junio de 12 a las 15:06

3 Respuestas

En lugar de saltar a través de los aros que se muestran arriba, normalmente declaro un método de instancia al que puedo llamar que, internamente, se ocupa de los reactivadores según sea necesario. De esa manera, cualquier bloque dado es de una sola vez, pero el re-disparador crea un nuevo bloque.

Siempre que la creación del bloque no sea terriblemente costosa, que no lo será si el estado proviene de lo que sea que encapsule el método de instancia, es lo suficientemente eficiente y mucho más simple.

- (void) retriggerMethod
{
     ... do stuff here, assuming you want to do it on first invocation ...
     dispatch_after( ..., ^{
         [self retriggerMethod];
     });
}

Puede reestructurarlo según sea necesario. Y puede agregar fácilmente una variable de instancia BOOL si desea protegerse contra reactivaciones simultáneas, etc.

Esto también proporciona un gancho conveniente para cancelar; simplemente agregue un BOOL a la instancia que indica si la próxima invocación realmente debería hacer algo y volver a programar.

Respondido el 12 de junio de 12 a las 22:06

Parece que usas mucho el bucle GCD como este. Me pregunto cuáles son las ventajas de usarlo en lugar de configurar un NSTimer. - gon

@Gon Preferencia personal, de verdad, pero hay algunas diferencias. dispatch_*() le permite controlar estrictamente la cola, por ejemplo. Como soy perezoso, también me gustan mucho los fragmentos pegados por la finalización del código de Xcode. :) - bbum

La respuesta de Jeffrey Thomas está cerca, pero bajo ARC, filtra el bloque y sin ARC, falla.

Sin ARC, un __block variable no retiene lo que hace referencia. Los bloques se crean en la pila. Entonces el callback variable apunta a un bloque en la pila. cuando pasas callback a dispatch_after la primera vez (fuera del bloque), dispatch_after hace con éxito una copia del bloque en el montón. Pero cuando se invoca esa copia, y pasa callback a dispatch_after de nuevo, callback es un puntero colgante (al bloque ahora destruido en la pila), y dispatch_after (generalmente) se bloqueará.

Con ARC, un __block variable de tipo bloque (como callback) automáticamente copia el bloque al montón. Para que no tengas el accidente. Pero con ARC, un __block La variable conserva el objeto (o bloque) al que hace referencia. Esto da como resultado un ciclo de retención: el bloque se referencia a sí mismo. Xcode le mostrará una advertencia sobre el recursivo dispatch_after call: "La captura de 'devolución de llamada' con fuerza en este bloque es probable que conduzca a un ciclo de retención".

Para solucionar estos problemas, puede copiar el bloque explícitamente (para moverlo de la pila al montón en MRC) y establecer callback a cero (bajo ARC) o liberarlo (bajo MRC) para evitar fugas:

    __block void (^callback)() = [^{
        if(stop_) {
            NSLog(@"all done");
#if __has_feature(objc_arc)
            callback = nil; // break retain cycle
#else
            [callback release];
#endif
        } else {
            NSLog(@"still going");
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);
        }
    } copy];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);

Obviamente puedes dejar el #if y simplemente use la rama apropiada para su administración de memoria.

Respondido el 12 de junio de 12 a las 21:06

Creo que este es el código que buscas:

__block void (^callback)();
callback = ^{
    bool ready = some_function();
    if( ready ) {
        do_smth_here()
    } else {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);
    }
};

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback);

Gracias a ^ Consejos y trucos de bloques

Respondido el 12 de junio de 12 a las 17:06

@robmayoff ¿Fuga? no lo veo Aquí no hay ciclo de retención. callback es retenido automáticamente por dispatch_after() y se libera automáticamente cuando se completa el bloque. Todas las retenciones y liberaciones se cancelan. No dudo que pueda haber algún defecto en la implementación de bloques que aguanten una referencia extra, pero habría que verlo en un analizador. - jeffery thomas

Mira mi respuesta. Por cierto, probé que su solución falla (bajo MRC) y se filtra (bajo ARC) usando el registro de historial malloc. - Rob Mayoff

@robmayoff ¿Qué puedo decir? El compilador y el tiempo de ejecución son el juez final. Buena solución. - jeffery thomas

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.