tl; dr le runloop effectue un défilement afin qu'il ne puisse plus gérer d'événements - à moins que vous ne régliez manuellement le minuteur pour qu'il puisse également se produire lorsque runloop gère des événements tactiles. Ou essayez une autre solution et utilisez GCD
Une lecture incontournable pour tout développeur iOS. Beaucoup de choses sont finalement exécutées via RunLoop.
Dérivé de la documentation d' Apple .
Qu'est-ce qu'une boucle de course?
Une boucle d'exécution ressemble beaucoup à son nom. C'est une boucle que votre thread entre et utilise pour exécuter des gestionnaires d'événements en réponse aux événements entrants
Comment la livraison des événements est-elle perturbée?
Étant donné que les minuteries et autres événements périodiques sont fournis lorsque vous exécutez la boucle d'exécution, le contournement de cette boucle perturbe la livraison de ces événements. L'exemple typique de ce comportement se produit chaque fois que vous implémentez une routine de suivi de la souris en entrant une boucle et en demandant à plusieurs reprises des événements à partir de l'application. Étant donné que votre code saisit directement les événements, plutôt que de laisser l'application distribuer ces événements normalement, les minuteries actives ne pourront pas se déclencher tant que votre routine de suivi de la souris n'est pas sortie et a renvoyé le contrôle à l'application.
Que se passe-t-il si le minuteur est déclenché lorsque la boucle d'exécution est en cours d'exécution?
Cela arrive BEAUCOUP DE FOIS, sans que nous nous en rendions compte. Je veux dire que nous avons réglé la minuterie pour qu'elle se déclenche à 10: 10: 10: 00, mais la boucle d'exécution exécute un événement qui prend jusqu'à 10: 10: 10: 05, donc la minuterie est déclenchée 10: 10: 10: 06
De même, si un minuteur se déclenche lorsque la boucle d'exécution est au milieu de l'exécution d'une routine de gestionnaire, le minuteur attend la prochaine fois dans la boucle d'exécution pour appeler sa routine de gestionnaire. Si la boucle d'exécution ne s'exécute pas du tout, le minuteur ne se déclenche jamais.
Le défilement ou tout autre élément qui maintient la boucle de course occupée changerait-il toutes les fois que ma minuterie va se déclencher?
Vous pouvez configurer des minuteries pour générer des événements une seule fois ou plusieurs fois. Une minuterie à répétition se replanifie automatiquement en fonction de l'heure de tir programmée, et non de l'heure de tir réelle. Par exemple, si une minuterie est programmée pour se déclencher à un moment donné et toutes les 5 secondes par la suite, l'heure de tir programmée tombera toujours sur les intervalles de temps d'origine de 5 secondes, même si l'heure de tir réelle est retardée. Si le temps de tir est tellement retardé qu'il manque un ou plusieurs des temps de tir programmés, le temporisateur est déclenché une seule fois pendant la période de temps manquée. Après le tir pour la période manquée, le chronomètre est reprogrammé pour la prochaine heure de tir programmée.
Comment puis-je changer le mode des RunLoops?
Vous ne pouvez pas. Le système d'exploitation se modifie tout seul pour vous. Par exemple, lorsque l'utilisateur appuie sur, le mode passe à eventTracking
. Lorsque l'utilisateur appuie sur terminé, le mode revient à default
. Si vous voulez que quelque chose soit exécuté dans un mode spécifique, c'est à vous de vous assurer que cela se produit.
Solution:
Lorsque l'utilisateur fait défiler, le mode Run Loop devient tracking
. Le RunLoop est conçu pour changer de vitesse. Une fois que le mode est défini sur eventTracking
, il donne la priorité (rappelez-vous que nous avons des cœurs CPU limités) pour toucher les événements. Il s'agit d'une conception architecturale des concepteurs du système d'exploitation .
Par défaut, les minuteries ne sont PAS programmées sur le tracking
mode. Ils sont programmés le:
Crée une minuterie et la programme sur la boucle d'exécution actuelle dans le
mode par défaut .
Le scheduledTimer
dessous fait ceci:
RunLoop.main.add(timer, forMode: .default)
Si vous voulez que votre minuterie fonctionne lors du défilement, vous devez faire soit:
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self,
selector: #selector(fireTimer), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .tracking)
Ou faites simplement:
RunLoop.main.add(timer, forMode: .common)
En fin de compte, faire l'une des choses ci-dessus signifie que votre thread n'est pas bloqué par les événements tactiles. ce qui équivaut à:
RunLoop.main.add(timer, forMode: .default)
RunLoop.main.add(timer, forMode: .eventTracking)
RunLoop.main.add(timer, forMode: .modal)
Solution alternative:
Vous pouvez envisager d'utiliser GCD pour votre minuterie, ce qui vous aidera à «protéger» votre code des problèmes de gestion de la boucle d'exécution.
Pour ne pas se répéter, utilisez simplement:
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
}
Pour les minuteries répétitives, utilisez:
Découvrez comment utiliser DispatchSourceTimer
Creuser plus profondément à partir d'une discussion que j'ai eue avec Daniel Jalkut:
Question: comment GCD (threads d'arrière-plan), par exemple un asyncAfter sur un thread d'arrière-plan, est-il exécuté en dehors du RunLoop? Je crois comprendre que tout doit être exécuté dans un RunLoop
Pas nécessairement - chaque thread a au plus une boucle d'exécution, mais peut avoir zéro s'il n'y a aucune raison de coordonner l'exécution "propriété" du thread.
Les threads sont une offre au niveau du système d'exploitation qui donne à votre processus la possibilité de répartir ses fonctionnalités sur plusieurs contextes d'exécution parallèles. Les boucles d'exécution sont une capacité au niveau de l'infrastructure qui vous permet de diviser davantage un seul thread afin qu'il puisse être partagé efficacement par plusieurs chemins de code.
En règle générale, si vous distribuez quelque chose qui s'exécute sur un thread, il n'aura probablement pas de boucle d'exécution à moins que quelque chose n'appelle [NSRunLoop currentRunLoop]
qui en créerait implicitement une.
En un mot, les modes sont essentiellement un mécanisme de filtre pour les entrées et les minuteries