![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
Un RunLoop est une boucle qui coordonne la réception et le traitement des événements entrants dans un thread spécifique.
Le RunLoop est présent dans chaque thread, mais par défaut, il est en mode veille et n'effectue aucun travail.
Le développeur peut l'exécuter si nécessaire, mais cela ne fonctionnera pas automatiquement. Pour ce faire, vous devrez écrire du code.
Tout d'abord, RunLoop est conçu pour gérer le flux des tâches entrantes et les exécuter au bon moment.
Ceci est particulièrement visible lorsque vous travaillez avec l'interface utilisateur, par exemple lors de l'utilisation de UIScrollView.
Par défaut, le RunLoop principal est toujours en cours d'exécution dans l'application ; il traite les messages du système et les transmet à l'application. Un exemple de tels messages peut être, par exemple, un événement lorsqu'un utilisateur clique sur l'écran.
Les threads auxiliaires nécessitent l’autodétermination de la nécessité d’un RunLoop. Si vous en avez besoin, vous devrez le configurer et l'exécuter vous-même. L'exécution de RunLoop par défaut n'est pas recommandée, elle n'est requise que dans les cas où nous avons besoin d'une interaction active avec les threads.
De plus, tous les minuteurs de l'application sont exécutés sur le runloop, donc si vous devez interagir avec eux dans votre application, vous devez absolument étudier les fonctionnalités du runloop.
RunLoop est une boucle et comporte plusieurs modes de fonctionnement qui aident le développeur à comprendre quand exécuter une tâche particulière.
Ainsi, RunLoop peut être dans les modes suivants :
Default
- Le mode par défaut, le flux est gratuit et des opérations importantes peuvent y être effectuées en toute sécurité.
Tracking
: le fil de discussion est occupé à effectuer un travail important. À ce stade, il est préférable de n’exécuter aucune tâche, ou du moins d’exécuter quelques petites tâches.
Initialization
- Ce mode est exécuté une fois lors de l'initialisation du flux.
EventReceive
- Il s'agit d'un mode interne de réception d'événements système, qui n'est généralement pas utilisé.
Common
: Il s'agit d'un mode d'espace réservé qui n'a aucune signification pratique.
Sur le RunLoop principal, ces modes sont automatiquement commutés ; le développeur peut les utiliser pour effectuer des tâches fastidieuses afin que l'utilisateur ne remarque pas le blocage de l'interface. Regardons un exemple.
La gestion du cycle d'exécution dans les autres RunLoop n'est pas entièrement automatique. Vous devez écrire du code pour un thread qui démarrera le cycle d'exécution à un moment approprié. En outre, vous devez répondre de manière appropriée aux événements et utiliser des boucles sans fin pour garantir que le cycle d'exécution ne s'arrête pas.
Nous avons un UIScrollView et nous devons effectuer une tâche volumineuse sur le thread principal afin que l'utilisateur ne remarque rien.
Nous pouvons accomplir la tâche de la manière habituelle :
DispatchQueue.main.async { sleep(2) self.tableView.refreshControl?.endRefreshing() }
Mais le résultat va être plutôt mauvais. L'utilisateur remarquera des retards importants dans l'application.
Cet effet négatif est dû au fait que nous exécutons une tâche sur le thread principal sans prêter attention à ce qui s'y passe en ce moment.
Pour cette raison, nous commençons à accomplir notre grande tâche au moment où l'utilisateur interagit avec l'interface. Ceci, bien sûr, conduit au fait que l'utilisateur voit l'interface se bloquer.
Cela peut être évité en utilisant le mécanisme RunLoop. Implémentons la même logique en utilisant ceci :
CFRunLoopPerformBlock(CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) { sleep(2) self.tableView.refreshControl?.endRefreshing() }
Laissez-moi vous expliquer ce qui se passe ici. La fonction CFRunLoopPerformBlock ajoute du code à exécuter via RunLoop. En plus du bloc de code lui-même, cette fonction possède 2 paramètres importants.
Le premier est responsable de la sélection du RunLoop qui doit exécuter la fonction. Dans cet exemple, « main » est utilisé.
Le second est responsable du mode dans lequel la tâche sera accomplie.
Il y a trois modes possibles au total :
Commun - combine le mode par défaut et le mode de suivi.
Le résultat de l'exécution du programme avec le code ci-dessus sera :
Lorsque l'utilisateur commence à interagir avec l'interface utilisateur (UI), la boucle d'exécution principale passe en mode « suivi » et suspend temporairement le traitement de tous les autres événements afin d'assurer la fluidité de l'interface. Une fois que l'utilisateur cesse d'interagir avec l'interface, la boucle d'exécution revient à son mode « par défaut » et reprend l'exécution de notre tâche.
Outre l’interface utilisateur, la boucle est également étroitement liée au fonctionnement des timers.
Tout minuteur de l'application fonctionne en boucle et vous devez faire très attention à ne pas commettre d'erreurs lorsque vous travaillez avec lui, surtout s'il est responsable de fonctionnalités importantes telles que le traitement des paiements.
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething }
La minuterie démarre par défaut en mode par défaut, elle peut donc cesser de fonctionner si l'utilisateur fait actuellement défiler le tableau. En effet, la boucle est actuellement en mode suivi. C'est pourquoi le code de l'exemple peut ne pas fonctionner correctement. Vous pouvez résoudre ce problème en ajoutant une minuterie en mode commun.
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in // makeSomething } RunLoop.main.add(timer, forMode: .common)
De plus, les minuteries peuvent ne pas se déclencher à l’heure prévue, voire pas du tout. En effet, RunLoop ne vérifie les minuteries qu'au début de chaque cycle. Si une minuterie se déclenche après que RunLoop ait dépassé cette étape, nous n'en serons informés qu'au début de la prochaine itération. Dans le même temps, plus la tâche s’exécute longtemps sur RunLoop, plus le délai sera long.
Pour résoudre ce problème, vous pouvez créer un nouveau thread, démarrer un RunLoop dans ce thread, puis ajouter un minuteur au thread sans ajouter d'autres tâches. De cette manière, le minuteur fonctionnera correctement.
let thread = Thread { let timer = Timer(timeInterval: 1.0, repeats: true) { timer in // makeSomething } RunLoop.current.add(timer, forMode: .default) RunLoop.current.run() } thread.start()
Dans cet article, nous avons examiné ce qu'est RunLoop et quels problèmes il résout dans les applications iOS. Un RunLoop est une boucle qui coordonne la réception et le traitement des événements entrants au sein d’un thread spécifique et dispose de plusieurs modes de fonctionnement.
Il est particulièrement utile lorsque vous travaillez avec l'interface utilisateur (UI) et les minuteries car il a la capacité d'exécuter des tâches au bon moment, ce qui permet d'éviter de « raccrocher » l'interface et d'assurer le bon fonctionnement des minuteries.
Bien que travailler avec Run Loop nécessite un codage supplémentaire, il s'agit d'un investissement rentable qui améliore l'efficacité et la stabilité de votre application.