ios - dispatchqueue - nsoperationqueue swift 3 example
Hace dispatch_async(dispatch_get_main_queue(), ^{…}); esperar hasta que termine? (7)
Tengo un escenario en mi aplicación, en el que quiero realizar una tarea que consume mucho tiempo y que consiste en un procesamiento de datos y una actualización de la interfaz de usuario en un método. Mi método se parece a esto,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
// UI UPDATE 1
// DATA PROCESSING 2
// UI UPDATE 2
// DATA PROCESSING 3
// UI UPDATE 3
}
Como es mucho tiempo, quise hacer el procesamiento de datos en el hilo de fondo, usando,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
Pero como tanto el procesamiento de datos como las actualizaciones de la interfaz de usuario están en el mismo método, quise mover solo las actualizaciones de la interfaz de usuario en el hilo principal usando,
dispatch_async(dispatch_get_main_queue(), ^{
Finalmente mi método se parece a esto,
- (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 esta realmente una buena práctica? ¿Cuál es la mejor manera de lograr esto?
PS Todas estas tres operaciones están interrelacionadas entre sí.
EDITAR: Lo siento chicos. Me 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, 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á poniendo en cola todos los bloques de UI en 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 en cola del bloque. Aunque no lo recomendaría para niveles múltiples.
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 actualizar la interfaz de usuario para esperar, entonces debe usar las versiones síncronas. Está bien tener un hilo de fondo esperando el hilo principal. Las actualizaciones de la interfaz de usuario deben ser muy rápidas.
No, no esperará.
Podría usar performSelectorOnMainThread:withObject:waitUntilDone:
OK, hay dos maneras de hacer eso:
// 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");
});
});
}
Si desea ejecutar una única operación en cola independiente y no le preocupan otras operaciones simultáneas, 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 prioridad alta, es decir, la cola se programará para su ejecución antes de cualquier prioridad predeterminada o cola de prioridad baja.
DISPATCH_QUEUE_PRIORITY_DEFAULT Los elementos enviados a la cola se ejecutarán con la prioridad predeterminada, es decir, la cola se programará para su ejecución después de que se hayan programado todas las colas de alta prioridad, pero antes de que se haya programado ninguna cola de baja prioridad.
DISPATCH_QUEUE_PRIORITY_LOW Los elementos enviados a la cola se ejecutarán con prioridad baja, es decir, la cola se programará para su ejecución una vez que se hayan programado todas las colas de prioridad predeterminada y de prioridad alta.
DISPATCH_QUEUE_PRIORITY_BACKGROUND Los elementos enviados a la cola se ejecutarán con prioridad de fondo, es decir, la cola se programará para su ejecución una vez que se hayan programado todas las colas de mayor prioridad y el sistema ejecutará los elementos de esta cola en un hilo con estado de fondo según el valor de conjunto (2) ( es decir, la E / S del disco se limita y la prioridad de programación del subproceso se establece en el valor más bajo).
Su doCalculationsAndUpdateUIs
propuesta realiza el procesamiento de datos y envía las actualizaciones de la interfaz de usuario a la cola principal. Supongo que ha enviado doCalculationsAndUpdateUIs
a una cola de fondo cuando lo llamó por primera vez.
Aunque técnicamente está bien, eso es un poco frágil, depende de que recuerdes enviarlo al fondo cada vez que lo llames: en cambio, te sugeriría que realices el envío al fondo y lo envíes a la cola principal desde el mismo Método, ya que hace que la lógica sea inequívoca y más robusta, etc.
Por lo tanto, podría parecer:
- (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 cuanto a si distribuye sus actualizaciones de UI de forma asíncrona con dispatch_async
(donde el proceso en segundo plano no esperará la actualización de UI) o de manera sincronizada con dispatch_sync
(donde esperará la actualización de UI), la pregunta es por qué querría hacerlo. sincrónicamente: ¿Realmente desea ralentizar el proceso en segundo plano mientras espera la actualización de la interfaz de usuario, o desea que el proceso en segundo plano continúe mientras se lleva a cabo la actualización de la interfaz de usuario?
En general, usted enviaría la actualización de la interfaz de usuario de forma asíncrona con dispatch_async
como lo ha usado en su pregunta original. Sí, ciertamente hay circunstancias especiales en las que necesita enviar el código de manera síncrona (por ejemplo, está sincronizando las actualizaciones a alguna propiedad de clase al realizar todas las actualizaciones en la cola principal), pero la mayoría de las veces, simplemente envía la actualización de la interfaz de usuario. Asíncrono y continuar. El código de envío de forma síncrona puede causar problemas (por ejemplo, puntos muertos) si se hace de manera descuidada, por lo que mi consejo general es que probablemente solo deba enviar las actualizaciones de la interfaz de usuario de forma sincronizada si hay una necesidad imperiosa de hacerlo, de lo contrario, debe diseñar su solución para poder enviarlas de forma asíncrona. .
En respuesta a su pregunta sobre si esta es la "mejor manera de lograr esto", es difícil para nosotros decirlo sin saber más sobre el problema comercial que se está resolviendo. Por ejemplo, si puede llamar a este doCalculationsAndUpdateUIs
varias veces, me inclino a usar mi propia cola de serie en lugar de una cola global concurrente, para garantizar que no se crucen entre sí. O si es posible que necesite la posibilidad de cancelar este doCalculationsAndUpdateUIs
cuando el usuario doCalculationsAndUpdateUIs
la escena o vuelva a llamar al método, entonces me inclino a usar una cola de operaciones que ofrezca capacidades de cancelación. Depende completamente de lo que estés tratando de lograr.
Pero, en general, el patrón de enviar de forma asíncrona una tarea complicada a una cola en segundo plano y luego enviar de forma asíncrona la actualización de la interfaz de usuario a la cola principal es muy común.
Tienes que poner tu cola principal despachando en el bloque que ejecuta el cálculo. Por ejemplo (aquí creo una cola de envío 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 crea una cola, no se olvide de dispatch_release
si está apuntando a una versión de iOS antes de la 6.0.
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.
});
});