Introduction : pourquoi votre iPhone ne se fige-t-il pas pendant que vous faites défiler votre fil d'actualité ?
\n\nVous êtes-vous déjà demandé pourquoi les applications iOS modernes ne ralentissent pas, même lorsqu'elles téléchargent simultanément des dizaines d'images, traitent des pressions et écrivent des données dans la base de données ? La réponse est simple : la concurrence. Dans le monde de Swift (et pas seulement), c'est l'un des sujets les plus brûlants. Et si vous écrivez en Swift, mais que vous confondez encore les threads, les goroutines et le nouveau async/await à la mode — bienvenue au club. Aujourd'hui, nous allons tout mettre à plat pour que votre code vole, et non rampe.
Dans cet article, nous ne nous contenterons pas d'énumérer des définitions sèches. Nous examinerons des exemples concrets, comparerons les approches et répondrons à la question principale : que choisir pour votre projet — le bon vieux GCD ou le nouveau Swift Concurrency ?
\n\n\n\nThreads : le fondement sur lequel tout repose
\n\nCommençons par les bases. Un thread est la plus petite unité de traitement que le système d'exploitation peut exécuter. Imaginez une chaîne de montage : un thread — une ligne sur laquelle les tâches se déplacent. Si vous avez un seul cœur de processeur, les threads s'exécuteront les uns après les autres, mais en basculant très rapidement, créant une illusion de parallélisme. Sur les processeurs multicœurs, les threads peuvent s'exécuter réellement en parallèle.
\n\nEn Swift, pendant longtemps, l'outil principal pour travailler avec les threads était Grand Central Dispatch (GCD). Vous créiez simplement une file d'attente et y envoyiez une closure :
\n\nDispatchQueue.global(qos: .background).async {\n // Travail lourd en arrière-plan\n let result = heavyComputation()\n DispatchQueue.main.async {\n // Mise à jour de l'UI sur le thread principal\n self.label.text = result\n }\n}\n\nTout est simple, n'est-ce pas ? Mais GCD a des nuances :
\n- \n
- Vous êtes responsable du retour sur le thread principal (facile à oublier et à provoquer un crash). \n
- Pas de prise en charge intégrée de l'annulation des tâches (il faut écrire des drapeaux manuellement). \n
- Difficile de gérer des chaînes d'opérations dépendantes sans imbrication de closures (ce qu'on appelle « callback hell »). \n
Néanmoins, GCD est toujours vivant et utilisé dans les projets legacy. Mais Swift moderne propose quelque chose de bien plus élégant.
\n\nDes goroutines en Swift ? Non, ce n'est pas Go
\n\nQuand on parle de goroutines, on pense généralement à Go. En Swift, il n'y a pas d'équivalent direct des goroutines, mais il y a des tâches légères (tasks) qui se comportent de manière très similaire. Elles ne sont pas liées à un thread spécifique — le système décide lui-même sur quel thread exécuter la tâche et peut les basculer entre les threads sans en créer de nouveaux à chaque appel.
\n\nContrairement aux threads, qui « pèsent » environ 8 Ko de pile chacun, les tâches de Swift Concurrency ne pèsent pratiquement rien. Vous pouvez lancer des milliers de tâches sans risquer de saturer la mémoire. C'est ce qu'on appelle « la concurrence sans douleur ».
\n\nRegardez à quoi cela ressemble :
\n\nTask {\n let image = await downloadImage(from: url)\n await MainActor.run {\n self.imageView.image = image\n }\n}\n\nVous avez remarqué ? Plus de DispatchQueue.main.async. Juste await MainActor.run. Propre, clair, sûr.
async/await : la révolution qui a eu lieu
\n\nAvec la sortie de Swift 5.5, la prise en charge intégrée de async/await est apparue. Ce n'est pas seulement du sucre syntaxique — c'est un changement de paradigme. Vous pouvez désormais écrire du code asynchrone comme s'il était synchrone, mais sans bloquer le thread.
\n\nDéclarer une fonction asynchrone est simple :
\n\nfunc fetchUserData() async throws -> User {\n let url = URL(string: "https://api.example.com/user")!\n let (data, _) = try await URLSession.shared.data(from: url)\n return try JSONDecoder().decode(User.self, from: data)\n}\n\nEt l'appeler est encore plus simple :
\n\nTask {\n do {\n let user = try await fetchUserData()\n print("User nam