Bienvenido, soy Miguel y hoy les traigo un post.
Índice
Comprender lo que significa enviar código al hilo principal y cuándo debe hacerlo
Muchos desarrolladores de iOS eventualmente se encuentran con un código que solicita DispatchQueue.main
.
A menudo está claro que esto se hace para actualizar la interfaz de usuario, pero he visto más de un puñado de casos en los que los desarrolladores lo utilizan DispatchQueue.main
como un intento de que su código funcione si la interfaz de usuario no se actualiza como esperaban o si se bloquean. ellos no entienden.
Por esa razón, me gustaría dedicar este post a las preguntas: ¿Cuándo debo usar DispatchQueue.main
? ¿Y por qué?
Comprender lo que hace la cola de despacho principal
En iOS, usamos colas de despacho para realizar trabajos en paralelo. Esto significa que puede tener varias colas de despacho ejecutándose al mismo tiempo y todas pueden realizar tareas simultáneamente. En general, las colas de despacho solo realizarán una tarea a la vez de una manera de primero en entrar, primero en salir. Sin embargo, pueden configurarse para programar el trabajo al mismo tiempo.
La cola de envío principal es una cola que ejecuta una tarea a la vez. También es la cola que realiza todas las actualizaciones de diseño. Si alguien habla de la importancia de no bloquear el hilo principal, lo que realmente está diciendo es que no quieren mantener ocupada la cola de despacho principal durante demasiado tiempo. Si mantiene ocupada la cola principal durante demasiado tiempo, notará que el rendimiento de desplazamiento de la aplicación se entrecorta, las animaciones tartamudean y los botones no responden.
La razón de esto es que la cola principal es responsable de todo lo relacionado con la interfaz de usuario, pero como ya establecimos, la cola principal es una cola en serie. Entonces, si la cola principal está ocupada haciendo algo, no puede responder a la entrada del usuario o dibujar nuevos cuadros de su animación.
Una gran cantidad de código en iOS es código que puede tardar un tiempo en ejecutarse. Por ejemplo, hacer una solicitud de red es un buen ejemplo de código que se ejecuta muy lento. Una vez que la llamada de red se envía al servidor, el código debe esperar una respuesta. Mientras espera, esa cola no hace nada más. Cuando la respuesta vuelve un par de segundos más tarde, la cola puede procesar los resultados y pasar a la siguiente tarea. Si realizó este trabajo en la cola principal, su aplicación no podrá dibujar ninguna IU o responder a la entrada del usuario durante toda la duración de la solicitud de red.
Podemos resumir toda esta sección en una breve oración: la cola principal es responsable de dibujar la interfaz de usuario y responder a la entrada del usuario. En la siguiente sección, veremos más de cerca cuándo debemos usar DispatchQueue.main
y qué hace exactamente.
Uso de DispatchQueue.main en la práctica
Puede asumir que las llamadas de red se realizan en su propia cola, lejos de la cola principal. Antes de continuar, quiero refrescar su memoria y mostrarle cómo se ve el código para una llamada de red:
URLSession.shared.dataTask(with: someURL) data, response, error in // implementation
La tarea de datos se crea con un cierre de finalización que se llama cuando finaliza la llamada de red.
Dado que es posible que los datos que devuelve el servidor aún deban procesarse, iOS llama a su cierre de finalización en una cola en segundo plano. Por lo general, esto es genial. No creo que haya ninguna situación en la que no desee realizar ningún procesamiento de los datos que recibe del servidor. A veces es posible que tenga que procesar más que otras veces, pero es muy poco común que no se procese. Sin embargo, una vez que haya terminado de procesar los datos, probablemente desee actualizar su interfaz de usuario.
Como sabe que no está en la cola principal cuando maneja una respuesta de red, deberá usar DipatchQueue.main
para asegurarse de que su interfaz de usuario se actualice en la cola principal. El siguiente código es un ejemplo de cómo volver a cargar una vista de tabla en la cola principal.
DispatchQueue.main.async self.tableView.reload()
Este código parece bastante simple, ¿verdad? Pero, ¿Qué está pasando realmente aquí?
DispatchQueue.main
es una instancia de DispatchQueue
. Todas las colas de despacho pueden programar la ejecución de su trabajo sync
o async
.
Normalmente, querrá programar el trabajo async
debido a que programar su trabajo sincrónicamente detendría la ejecución del hilo actual, espere a que el hilo de destino ejecute el cierre al que le pasa sync
y luego reanudar el hilo actual. Utilizando async
permite que el hilo actual se reanude mientras que el hilo de destino puede programar y realizar su cierre cuando sea necesario. Veamos un ejemplo:
func executesAsync() { var result = 0 DispatchQueue.global().async { result = 10 * 10 } print(result) // 0 } func executesSync() { var result = 0 DispatchQueue.global().sync { result = 10 * 10 } print(result) // 100 }
Ambas funciones anteriores son muy similares. La principal diferencia aquí es executesAsync
se envía a la cola principal de forma asincrónica, lo que provoca result
para ser impreso antes result
se actualiza.
La función executesSync
se envía a la cola principal sincrónicamente, lo que da como resultado la ejecución de executesSync
se pausará hasta que el cierre pase a DispatchQueue.main.sync
termina de ejecutar. Esto significa result
se actualiza cuando print
se llama.
Piense en el ejemplo anterior en el contexto de recargar una vista de tabla. Si usamos sync
en lugar de async
, la ejecución del cierre de finalización de llamadas de red se detiene mientras el hilo principal recarga la vista de tabla.
Una vez que se recarga la vista de tabla, se reanuda la ejecución del cierre. Mediante el uso async
, el cierre de finalización continúa su ejecución, y la vista de tabla se recargará cuando la cola principal tenga tiempo para hacerlo. Esto, con suerte, es casi instantáneo porque si lleva un tiempo, probablemente sea un síntoma de bloqueo del hilo principal.
Saber cuándo utilizar DispatchQueue.main
Ahora que sabe cuál es la cola principal y ha visto un ejemplo de cómo y cuándo DispatchQueue.main
se usa, ¿cómo sabe cuándo debe usarla DispatchQueue.main
en su proyecto?
La respuesta más simple es usarlo siempre cuando esté actualizando la interfaz de usuario en un método delegado o cierre de finalización porque no controla cómo o cuándo se llama a ese código.
Además, Xcode bloqueará su aplicación si detecta que está haciendo un trabajo de interfaz de usuario fuera del hilo principal. Si bien esta es una característica muy conveniente que me ayudó a prevenir errores de vez en cuando, no es confiable el 100% del tiempo. Hay mejores formas de asegurarse de que su código se ejecute en el hilo principal cuando sea necesario.
Recuerde siempre enviar su cola a la cola principal de forma asincrónica usando DispatchQueue.main.async
para evitar bloquear el hilo actual, e incluso potencialmente bloquear su aplicación, lo que puede suceder si llama DispatchQueue.main.sync
desde un código que ya está en la cola principal. Enviar a la cola principal con async
no conlleva el mismo riesgo, incluso si ya está en la cola principal.
Veamos un último ejemplo. Si obtiene los permisos de notificación push actuales de un usuario o solicita sus contactos, sabrá que la operación se ejecuta de forma asincrónica y puede llevar un tiempo. Si desea actualizar la interfaz de usuario en el cierre de finalización que se usa para estas operaciones, es mejor asegurarse explícitamente de que las actualizaciones de la interfaz de usuario se realicen en la cola principal envolviendo las actualizaciones de la interfaz de usuario en un DispatchQueue.main.async
bloque.
En resumen
Escribir aplicaciones que utilicen varias colas puede resultar realmente complicado. Como regla general, tenga en cuenta que la cola principal está reservada para el trabajo de la interfaz de usuario. Eso no significa que todo el trabajo que no sea de la interfaz de usuario deba salir de la cola principal, pero sí significa que todo el trabajo de la interfaz de usuario estará en la cola principal y que el resto del trabajo puede estar en otro lugar si lo desea (por ejemplo, si saber que una operación puede tardar un poco en completarse).
En otras palabras, la respuesta corta a la pregunta del principio de este artículo es que debe usar DispatchQueue.main
para enviar trabajo relacionado con la interfaz de usuario desde las colas no principales a la cola principal.
Gracias por leer.
Añadir comentario