Home » Guías » Los cambios de Swift 3 (I): Grand Central Dispatch
Banners GCD

Los cambios de Swift 3 (I): Grand Central Dispatch

Comenzamos un especial semanal donde repasaremos uno a uno los más importantes cambios de Swift 3 como lenguaje, con respecto a sus versiones anteriores. Las cosas que han cambiado de formas no compatibles y que nos obligan a re-aprender cómo trabajar con alguna librería o característica.

En este primer artículo vamos a ver Grand Central Dispatch, una librería de gestión de hilos desarrollada en C y que hasta la versión 2 de Swift era una API bastante poco amigable y poco práctica. Su cambio, por otro lado, se engloba en uno de los grandes cambios del lenguaje: el paso de variables y métodos estáticos a usar una orientación a objetos real e invocación de métodos desde los propios objetos.

Concepto general

GCD es la librería que se encarga de la programación concurrente, es decir, de cómo podemos lanzar diferentes procesos de nuestro programa en diferentes hilos de ejecución para que se ejecuten a la vez. Cuando tenemos una CPU con múltiples núcleos e incluso estos núcleos tienen varios hilos, podemos aprovechar estos canales para lanzar procesos que puedan ejecutarse simultáneamente y que aprovechen mejor el rendimiento de nuestros equipos.

Tenemos que pensar que una CPU no trabaja de manera lineal, si no que hace pequeñas pausas de procesamiento para gestionar tareas secundarias. Si la CPU hiciera solo una cosa a la vez, nos encontraríamos con el problema que hasta que un proceso de carga, grabación, gestión o cualquier otro no terminara, nuestro ordenador dejaría de responder, no veríamos nada en pantalla, no podríamos escribir en el teclado… La base de la computación desde sus inicios es que las CPUs hacen pequeñas pausas durante sus procesos para atender otras peticiones y con ello darnos la sensación que todo funciona a la vez, pero no es así. La CPU no hace muchas cosas a la vez, va cambiando de tarea varias veces por segundo para atender a todo el mundo y que tengamos la sensación que atiende a todos a la vez (aunque no sea así).

Con la llegada de los procesadores de múltiples hilos y de múltiples núcleos, fue cuando los procesadores pudieron empezar a, esta vez sí, hacer tareas simultáneas. Y eso es lo que gestiona GDC: cómo gestionamos o enviamos tareas a hilos diferentes al principal (o incluso al principal estando en un secundario) para conseguir ser más productivo y a su vez, controlar cómo cada uno de estos procesos devuelve (o no) posibles respuestas.

Un ejemplo de uso de GDC

Para entender el uso de GDC vamos a comentar un ejemplo práctico que vemos en muchas ocasiones y que nos pasa desapercibido: las barras de progreso. Normalmente este elemento nos informa como usuarios que un proceso se está realizando para que nuestro juego cargue, para que la app se inicie, para que se procesen algunos datos necesarios… Imaginemos un proceso en que debemos actualizar registros en una base de datos, un proceso que por su idiosincrasia, es muy pesado para el sistema.

Si hacemos el proceso de actualización y a la vez tenemos un objeto UIProgressView al que actualizamos la propiedad progress, veremos que el proceso se realiza pero la barra de tareas no se dibuja. O si lo hace, no se refresca a pesar que el código que nosotros veamos esté correcto. ¿Qué está sucediendo? Que el proceso de actualización de la base de datos está ocupando completamente el hilo asignado (el principal en este caso o main) y por lo tanto, cualquier otro proceso que vaya en el mismo no dispone de pausas de tiempo (en su hilo) para ejecutarse.

¿Cómo lo solucionamos? Haciendo una división y poniendo el proceso de mayor carga en un hilo diferente al principal, en este caso el que conoceríamos como hilo de proceso en segundo plano o background que definimos con una propiedad al crear una cola de despachamiento de procesos o DispatchQueue. ¿Cómo le decimos qué proceso queremos en cada hilo? A través de closures. Enviamos uno para cada hilo y desde dentro de uno podemos crear un hilo que llame a otro.

En este ejemplo, de una app real, estamos pasando todos los datos de una base de datos SQLite3 a otra. Tenemos una base de datos antigua etiquetada en la variable old en la que borramos los datos que tenía en ese momento para luego volcar uno a uno los mismos datos de otra base de datos instanciada en el objeto new. Lo que es un volcado convencional registro a registro.

Al principio definimos un objeto UIProgressView, le damos una serie de propiedades de inicio y luego creamos, usando interfaces fluidas, una cola para el background a la que asignamos un proceso asíncrono. Y dentro de este, creamos otro proceso asíncrono para refrescar la barra de progreso con el método setProgress pero dentro de una cola asociada al hilo principal, accesible desde el método main de DispatchQueue.

Al hacer esto, tenemos un proceso que se ejecuta en segundo plano (la migración) pero el hilo principal queda libre para refrescar la interfaz. ¿Por qué pasamos el proceso pesado a background? Porque el sistema pinta la interfaz siempre en el hilo principal (main) por lo es ahí donde accedemos al redibujado de la misma. De esta forma, aunque el proceso sea muy pesado (como este en concreto) podremos refrescar la interfaz de forma independiente.

Cambio de Swift 2 a 3

Si en Swift 2 queríamos crear un proceso asíncrono teníamos que hacer lo siguiente:

Estábamos definiendo una variable con una constante del sistema QOS_CLASS_BACKGROUND que nos identificaba el tipo de proceso que íbamos a crear. Luego, usábamos un constructor de la clase dispatch_get_global_queue y le pasábamos el tipo de proceso y un valor de prioridad. Tras esto, invocamos a una función independiente dispatch_async a la que había que pasarle la cola que acabábamos de crear y dentro de ella, poner el closure para el proceso en segundo plano. Bastante poco orientado a objetos e ineficiente, al tener que crear y usar todo como variables y funciones globales.

¿Cómo se haría en Swift 3? Como ya hemos visto: creando un objeto DispatchQueue al que incluso podemos poner una etiqueta. Dicha clase creará la cola de proceso y luego solo tenemos que invocar sus métodos, como el método async. En la propia inicialización de la cola, ya le decimos el qos apuntado a un proceso en según qué hilo.

Mucho más sencillo e intuitivo, con una orientación a objetos y especificación más modernas y reales.

Conclusiones

El cambio es importante aunque sencillo de entender. La especificación y forma ha cambiado radicalmente. Ahora hay que usar la clase DispatchQueue y con ella invocamos los diferentes métodos que necesitemos, en vez de antes que eran todo constantes y funciones fuera de todo ámbito y no orientadas. De hecho, a estas funciones sin ámbito siempre había que pasarles el objeto creado con la cola, lo que no tiene mucho sentido.

Ya sabéis que si queréis aprender más de Swift 3, nos podéis leer en la web o adquirir nuestro libro “Aprendiendo Swift 3” en formato de tapa blanda (físico) o en digital. Y como siempre, probad, experimentad y Good Apple Coding.

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Swift 3 Cambios (II)

Los cambios en Swift 3 (II): Subcadenas

Nueva entrega del especial de los cambios de Swift 3 respecto a sus versiones anteriores. Analizamos en profundidad los cambios en el uso de subcadenas en Swift 3, comparando la forma en que se usaban en Swift 2. Grandes cambios tanto en la nomenclatura de los métodos a usar, formas de extraer el tipo String.Index e incluso de crear los rangos que nos permitan extraer un trozo concreto de una cadena.