Hace dispatch_async (dispatch_get_main_queue (), ^ {…}); esperar hasta que termine?

Tengo un escenario en mi aplicación, en el que quiero hacer una tarea que consume mucho tiempo, que consiste en algún procesamiento de datos, así como la actualización de la interfaz de usuario, en un método. Mi método se ve así,

- (void)doCalculationsAndUpdateUIs { // DATA PROCESSING 1 // UI UPDATE 1 // DATA PROCESSING 2 // UI UPDATE 2 // DATA PROCESSING 3 // UI UPDATE 3 } 

Como me lleva mucho tiempo, quería hacer el procesamiento de datos en el hilo de fondo, usando,

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{ 

Pero como el procesamiento de datos y las actualizaciones de UI están en el mismo método, quería mover solo las actualizaciones de UI en el hilo principal usando,

 dispatch_async(dispatch_get_main_queue(), ^{ 

Finalmente mi método se ve así,

 - (void)doCalculationsAndUpdateUIs { // DATA PROCESSING 1 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 1 }); /* I expect the control to come here after UI UPDATE 1 */ // DATA PROCESSING 2 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 2 }); /* I expect the control to come here after UI UPDATE 2 */ // DATA PROCESSING 3 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 3 }); } 

¿Esto realmente funciona? ¿Es esto realmente una buena práctica? ¿Cuál es la mejor manera de lograr esto?

PD Todas estas tres operaciones están interrelacionadas entre sí.


EDITAR: Lo siento chicos. He perdido una línea en el código anterior . Mi código actual se ve así.

 - (void)doCalculationsAndUpdateUIs { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // DATA PROCESSING 1 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 1 }); /* I expect the control to come here after UI UPDATE 1 */ // DATA PROCESSING 2 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 2 }); /* I expect the control to come here after UI UPDATE 2 */ // DATA PROCESSING 3 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATE 3 }); }); } 

Una vez más, realmente me disculpo por la confusión.

No, no espera y la forma en que lo está haciendo en esa muestra no es una buena práctica.

dispatch_async siempre es asíncrono . Es solo que está enquerando todos los bloques de UI a la misma cola para que los diferentes bloques se ejecuten en secuencia pero en paralelo con su código de procesamiento de datos.

Si desea que la actualización espere, puede usar dispatch_sync lugar.

 // This will wait to finish dispatch_sync(dispatch_get_main_queue(), ^{ // Update the UI on the main thread. }); 

Otro enfoque sería anidar enqueueing el bloque. Sin embargo, no lo recomendaría para múltiples niveles.

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Background work dispatch_async(dispatch_get_main_queue(), ^{ // Update UI dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Background work dispatch_async(dispatch_get_main_queue(), ^{ // Update UI }); }); }); }); 

Si necesita la interfaz de usuario actualizada para esperar, debe usar las versiones sincrónicas. Está bastante bien tener un hilo de fondo esperando el hilo principal. Las actualizaciones de UI deben ser muy rápidas.

Tienes que poner el despacho de cola principal en el bloque que ejecuta el cálculo. Por ejemplo (aquí creo una cola de despacho y no uso una global):

 dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL); dispatch_async(queue, ^{ // Do some computation here. // Update UI after computation. dispatch_async(dispatch_get_main_queue(), ^{ // Update the UI on the main thread. }); }); 

Por supuesto, si creas una cola, no te olvides de dispatch_release si estás apuntando a una versión de iOS anterior a la versión 6.0.

La propuesta de doCalculationsAndUpdateUIs procesa y distribuye las actualizaciones de UI a la cola principal. Supongo que ha enviado doCalculationsAndUpdateUIs a una cola en segundo plano cuando lo llamó por primera vez.

Aunque técnicamente bien, eso es un poco frágil, dependiendo de que recuerdes enviarlo a un segundo plano cada vez que lo llames: en cambio, te sugiero que hagas tu envío a segundo plano y lo envíes a la cola principal desde dentro del mismo método, ya que hace la lógica inequívoca y más robusta, etc.

Por lo tanto, podría verse así:

 - (void)doCalculationsAndUpdateUIs { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{ // DATA PROCESSING 1 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATION 1 }); /* I expect the control to come here after UI UPDATION 1 */ // DATA PROCESSING 2 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATION 2 }); /* I expect the control to come here after UI UPDATION 2 */ // DATA PROCESSING 3 dispatch_async(dispatch_get_main_queue(), ^{ // UI UPDATION 3 }); }); } 

En términos de si envía sus actualizaciones de UI de forma asincrónica con dispatch_async (donde el proceso en segundo plano no esperará la actualización de UI) o sincrónicamente con dispatch_sync (donde esperará la actualización de UI), la pregunta es por qué querría hacerlo Sincrónico: ¿Realmente desea ralentizar el proceso en segundo plano mientras espera la actualización de UI, o le gustaría que el proceso en segundo plano continúe mientras se lleva a cabo la actualización de UI?

En general, enviaría la actualización de la UI de forma asíncrona con dispatch_async como lo hizo en su pregunta original. Sí, ciertamente hay circunstancias especiales en las que necesita enviar código de forma síncrona (por ejemplo, está sincronizando las actualizaciones de una propiedad de clase al realizar todas las actualizaciones en la cola principal), pero la mayoría de las veces envía la actualización de UI asincrónicamente y continuar. Despachar el código de forma sincrónica puede causar problemas (por ejemplo, lockings), por lo que mi consejo general es que probablemente solo deba enviar actualizaciones de interfaz de usuario de forma sincrónica si hay alguna necesidad imperiosa de hacerlo, de lo contrario debería diseñar su solución para que pueda despacharlos de manera asincrónica. .


En respuesta a su pregunta sobre si esta es la “mejor manera de lograr esto”, es difícil para nosotros decir sin saber más sobre el problema comercial que se está resolviendo. Por ejemplo, si puede estar llamando a esto doCalculationsAndUpdateUIs varias veces, me inclinaría a utilizar mi propia cola serie en lugar de una cola global simultánea, para garantizar que no se superpongan entre sí. O si necesita la posibilidad de cancelar esta función doCalculationsAndUpdateUIs cuando el usuario doCalculationsAndUpdateUIs la escena o llama al método nuevamente, entonces podría inclinarme a utilizar una cola de operación que ofrezca capacidades de cancelación. Depende completamente de lo que estás tratando de lograr.

Pero, en general, el patrón de despachar asíncronamente una tarea complicada a una cola en segundo plano y luego distribuir asincrónicamente la actualización de la interfaz de usuario a la cola principal es muy común.

Si desea ejecutar una sola operación en cola independiente y no le preocupan otras operaciones concurrentes, puede usar la cola simultánea global:

 dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Esto devolverá una cola concurrente con la prioridad dada como se describe en la documentación:

DISPATCH_QUEUE_PRIORITY_HIGH Los elementos enviados a la cola se ejecutarán con alta prioridad, es decir, la cola se progtwigrá para la ejecución antes de cualquier prioridad predeterminada o cola de baja prioridad.

DISPATCH_QUEUE_PRIORITY_DEFAULT Los elementos enviados a la cola se ejecutarán con la prioridad predeterminada, es decir, la cola se progtwigrá para su ejecución después de que se hayan progtwigdo todas las colas de alta prioridad, pero antes de progtwigr cualquier cola de baja prioridad.

DISPATCH_QUEUE_PRIORITY_LOW Los artículos enviados a la cola se ejecutarán con baja prioridad, es decir, la cola se progtwigrá para la ejecución después de que se hayan progtwigdo todas las prioridades predeterminadas y las colas de prioridad alta.

DISPATCH_QUEUE_PRIORITY_BACKGROUND Los artículos enviados a la cola se ejecutarán con prioridad de fondo, es decir, la cola se progtwigrá para su ejecución después de que se hayan progtwigdo todas las colas de mayor prioridad y el sistema ejecutará elementos en esta cola en un hilo con estado de fondo según setpriority (2) ( es decir, la E / S de disco se regula y la prioridad de progtwigción de la secuencia se establece en el valor más bajo).

No, no esperará.

Puede usar performSelectorOnMainThread:withObject:waitUntilDone:

 dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL); dispatch_async(queue, ^{ // Do some computation here. // Update UI after computation. dispatch_async(dispatch_get_main_queue(), ^{ // Update the UI on the main thread. }); }); 

OK, hay dos formas de hacerlo:

 // GLOBAL_CONCURRENT_QUEUE - (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE { dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(globalConcurrentQ, ^{ // DATA PROCESSING 1 sleep(1); NSLog(@"Hello world chekpoint 1"); dispatch_sync(dispatch_get_main_queue(), ^{ // UI UPDATION 1 sleep(1); NSLog(@"Hello world chekpoint 2"); }); /* the control to come here after UI UPDATION 1 */ sleep(1); NSLog(@"Hello world chekpoint 3"); // DATA PROCESSING 2 dispatch_sync(dispatch_get_main_queue(), ^{ // UI UPDATION 2 sleep(1); NSLog(@"Hello world chekpoint 4"); }); /* the control to come here after UI UPDATION 2 */ sleep(1); NSLog(@"Hello world chekpoint 5"); // DATA PROCESSING 3 dispatch_sync(dispatch_get_main_queue(), ^{ // UI UPDATION 3 sleep(1); NSLog(@"Hello world chekpoint 6"); }); }); } // SERIAL QUEUE - (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE { dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL); dispatch_async(serialQ, ^{ // DATA PROCESSING 1 sleep(1); NSLog(@"Hello world chekpoint 1"); dispatch_sync(dispatch_get_main_queue(), ^{ // UI UPDATION 1 sleep(1); NSLog(@"Hello world chekpoint 2"); }); sleep(1); NSLog(@"Hello world chekpoint 3"); // DATA PROCESSING 2 dispatch_sync(dispatch_get_main_queue(), ^{ // UI UPDATION 2 sleep(1); NSLog(@"Hello world chekpoint 4"); }); }); }