while utiliza tipos que programacion para iteraciones for ejemplos decisiones con bucle anidadas objective-c cocoa cocoa-touch grand-central-dispatch nsrunloop

objective-c - utiliza - tipos de decisiones en programacion



¿Cómo se programa un bloque para que se ejecute en la siguiente iteración del ciclo de ejecución? (4)

Quiero poder ejecutar un block en la siguiente iteración de ciclo de ejecución. No es tan importante si se ejecuta al principio o al final del siguiente ciclo de ejecución, solo que la ejecución se aplaza hasta que todo el código en el ciclo de ejecución actual haya terminado de ejecutarse.

Sé que lo siguiente no funciona porque se intercala con el ciclo de ejecución principal para que mi código se ejecute en el siguiente ciclo de ejecución, pero puede que no.

dispatch_async(dispatch_get_main_queue(),^{ //my code });

Lo siguiente creo que sufre el mismo problema que el anterior:

dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void){ //my code });

Ahora creo que lo siguiente funcionaría, ya que se coloca al final del ciclo de ejecución actual (corríjanme si me equivoco), ¿funcionaría realmente?

[self performSelector:@selector(myMethod) withObject:nil afterDelay:0];

¿Qué tal un temporizador con un intervalo de 0 ? La documentación indica: If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead. ¿Esto se traduce en garantizar la ejecución en la siguiente iteración del ciclo de ejecución?

[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(myMethod) userInfo:nil repeats:NO];

Esas son todas las opciones que puedo pensar, pero aún no estoy más cerca de ejecutar un bloque (en lugar de llamar a un método) en la próxima iteración de ciclo de ejecución con la garantía de que no será más pronto.


Es posible que no esté al tanto de todo lo que hace el ciclo de ejecución en cada iteración. (¡No era antes de que investigara esta respuesta!) CFRunLoop casualidad, CFRunLoop forma parte del paquete de código abierto CoreFoundation , por lo que podemos ver exactamente qué implica. El ciclo de ejecución se ve más o menos así:

while (true) { Call kCFRunLoopBeforeTimers observer callbacks; Call kCFRunLoopBeforeSources observer callbacks; Perform blocks queued by CFRunLoopPerformBlock; Call the callback of each version 0 CFRunLoopSource that has been signalled; if (any version 0 source callbacks were called) { Perform blocks newly queued by CFRunLoopPerformBlock; } if (I didn''t drain the main queue on the last iteration AND the main queue has any blocks waiting) { while (main queue has blocks) { perform the next block on the main queue } } else { Call kCFRunLoopBeforeWaiting observer callbacks; Wait for a CFRunLoopSource to be signalled OR for a timer to fire OR for a block to be added to the main queue; Call kCFRunLoopAfterWaiting observer callbacks; if (the event was a timer) { call CFRunLoopTimer callbacks for timers that should have fired by now } else if (event was a block arriving on the main queue) { while (main queue has blocks) { perform the next block on the main queue } } else { look up the version 1 CFRunLoopSource for the event if (I found a version 1 source) { call the source''s callback } } } Perform blocks queued by CFRunLoopPerformBlock; }

Puede ver que hay una variedad de formas de enganchar en el ciclo de ejecución. Puede crear un CFRunLoopObserver para ser llamado para cualquiera de las "actividades" que desee. Puede crear una versión 0 CFRunLoopSource y señalizarla inmediatamente. Puede crear un par conectado de CFMessagePorts , envolver uno en una versión 1 CFRunLoopSource y enviarle un mensaje. Puede crear un CFRunLoopTimer . Puede CFRunLoopPerformBlock bloques en cola utilizando dispatch_get_main_queue o CFRunLoopPerformBlock .

Tendrá que decidir cuál de estas API usar en función de cuándo está programando el bloqueo y cuándo necesita que se llame.

Por ejemplo, los toques se manejan en una fuente de versión 1, pero si maneja el toque actualizando la pantalla, esa actualización no se realiza hasta que se haya confirmado la transacción de Core Animation, lo que ocurre en un observador kCFRunLoopBeforeWaiting .

Ahora suponga que desea programar el bloqueo mientras manipula el toque, pero desea que se ejecute después de que se haya comprometido la transacción.

Puede agregar su propio CFRunLoopObserver para la actividad kCFRunLoopBeforeWaiting , pero este observador puede ejecutarse antes o después del observador de Core Animation, dependiendo del orden que especifique y del orden que especifique Core Animation. (Core Animation actualmente especifica un pedido de 2000000, pero eso no está documentado por lo que podría cambiar.)

Para asegurarse de que su bloque se ejecuta después del observador de Core Animation, incluso si su observador se ejecuta antes del observador de Core Animation, no llame al bloque directamente en la devolución de llamada del observador. En su lugar, use dispatch_async en ese punto para agregar el bloque a la cola principal. Poner el bloque en la cola principal forzará al ciclo de ejecución a despertar de su "espera" inmediatamente. Ejecutará cualquier observador kCFRunLoopAfterWaiting , y luego drenará la cola principal, en cuyo momento ejecutará su bloque.


Me escribí una categoría NSObject que acepta un valor de retardo variable, basado en otra pregunta de . Al pasar un valor de cero, efectivamente está haciendo que el código se ejecute en la próxima iteración de runloop disponible.


No creo que haya ninguna API que te permita garantizar que el código se ejecute en el próximo turno de evento. También me llama la atención por qué necesitas una garantía de que nada más se ha ejecutado en el ciclo, el principal en particular.

También puedo confirmar que con el perforSelector: withObject: afterDelay utiliza un temporizador basado en runloop, y tendrá un comportamiento funcionalmente similar a dispatch_async''ing en dispatch_get_main_queue ().

editar:

En realidad, después de volver a leer su pregunta, parece que solo necesita completar el ciclo actual de runloop. Si eso es cierto, entonces dispatch_async es exactamente lo que necesita. De hecho, todo el código anterior garantiza que se completará el ciclo de ejecución actual .


dispatch_async en mainQueue es una buena sugerencia, pero no se ejecuta en el siguiente ciclo de ejecución, se inserta en la ejecución actual del ciclo.

Para obtener el comportamiento que busca, deberá recurrir a la forma tradicional:

[self performSelector:@selector(myMethod) withObject:nil afterDelay:0];

Esto también le da la ventaja adicional de que puede cancelarse usando cancelPreviousPerforms de NSObject.