NSDefaultRunLoopMode et NSRunLoopCommonModes

114

Chaque fois que j'essaye de télécharger un gros fichier derrière UIScrollView, MPMapViewou quelque chose du genre, le processus de téléchargement s'arrête dès que je touche l'écran de l'iPhone. Heureusement, un article de blog génial de Jörn suggère une option alternative, en utilisant NSRunLoopCommonModespour la connexion.

Cela me permet d'examiner en détail les deux modes, NSDefaultRunLoopMode et NSRunLoopCommonModes, mais le document Apple n'explique pas gentiment, à part dire

NSDefaultRunLoopMode

Le mode pour traiter les sources d'entrée autres que les objets NSConnection. C'est le mode de boucle d'exécution le plus couramment utilisé.

NSRunLoopCommonModes

Les objets ajoutés à une boucle d'exécution en utilisant cette valeur comme mode sont surveillés par tous les modes de boucle d'exécution qui ont été déclarés comme membre de l'ensemble des modes «communs»; voir la description de CFRunLoopAddCommonMode pour plus de détails.

CFRunLoopAddCommonMode

Les sources, les minuteries et les observateurs sont enregistrés dans un ou plusieurs modes de boucle d'exécution et ne s'exécutent que lorsque la boucle d'exécution s'exécute dans l'un de ces modes. Les modes communs sont un ensemble de modes de boucle d'exécution pour lesquels vous pouvez définir un ensemble de sources, de minuteries et d'observateurs partagés par ces modes. Au lieu d'enregistrer une source, par exemple, dans chaque mode de boucle d'exécution spécifique, vous pouvez l'enregistrer une fois dans le pseudo-mode commun de la boucle d'exécution et elle sera automatiquement enregistrée dans chaque mode de boucle d'exécution dans l'ensemble de modes communs. De même, lorsqu'un mode est ajouté à l'ensemble des modes communs, toutes les sources, minuteries ou observateurs déjà enregistrés dans le pseudo-mode commun sont ajoutés au mode commun nouvellement ajouté.

Quelqu'un peut-il expliquer les deux en langage humain?

Stkim1
la source

Réponses:

204

Une boucle d'exécution est un mécanisme qui permet au système de réveiller les threads en veille afin qu'ils puissent gérer les événements asynchrones. Normalement, lorsque vous exécutez un thread (à l'exception du thread principal), il existe une option pour démarrer le thread dans une boucle d'exécution ou non. Si le thread exécute une opération de tri ou de longue durée sans interaction avec les événements externes et sans minuteurs, vous n'avez pas besoin d'une boucle d'exécution, mais si votre thread doit répondre aux événements entrants, il doit être attaché à une boucle d'exécution afin de réveillez le fil lorsque de nouveaux événements arrivent. C'est le cas des NSURLConnectionthreads générés, car ils ne se réveillent que sur les événements entrants (du réseau).

Chaque thread peut être associé à plusieurs boucles d'exécution ou peut être associé à une boucle d'exécution spécifique qui peut être définie pour fonctionner dans différents modes. Un «mode de boucle d'exécution» est une convention utilisée par le système d'exploitation pour établir certaines règles pour savoir quand livrer certains événements ou les collecter pour être livrés plus tard.

Habituellement, toutes les boucles d'exécution sont réglées sur le "mode par défaut" qui établit une manière par défaut de gérer les événements d'entrée. Par exemple: dès qu'un événement de déplacement de la souris (Mac OS) ou tactile (sur iOS) se produit, le mode de cette boucle d'exécution est défini sur le suivi des événements; cela signifie que le thread ne sera pas réveillé lors de nouveaux événements réseau mais que ces événements seront livrés plus tard lorsque l'événement d'entrée utilisateur se terminera et que la boucle d'exécution sera à nouveau définie en mode par défaut; c'est évidemment un choix fait par les architectes du système d'exploitation de donner la priorité aux événements utilisateur plutôt qu'aux événements d'arrière-plan.

Si vous décidez de modifier le mode de boucle d'exécution pour votre NSURLConnectionthread, en utilisant scheduleInRunLoop:forModes:, vous pouvez affecter le thread à un mode de boucle d'exécution spécial , plutôt qu'à la boucle d'exécution par défaut spécifique. Le pseudo-mode spécial appelé NSRunLoopCommonModesest utilisé par de nombreuses sources d'entrée, y compris le suivi des événements. Par exemple, l'affectation NSURLConnectionde l'instance de au mode commun signifie associer ses événements au "mode suivi" en plus du "mode par défaut". Un avantage / inconvénient d'associer des threads NSRunLoopCommonModesest que le thread ne sera pas bloqué par des événements tactiles.

De nouveaux modes peuvent être ajoutés aux modes communs, mais il s'agit d'une opération de bas niveau.

Je voudrais terminer en ajoutant quelques notes:

  • En règle générale, nous devons utiliser un ensemble d'images ou de miniatures téléchargées à partir du réseau avec une vue de tableau. On peut penser que le téléchargement de ces images depuis le réseau alors que la vue du tableau défile pourrait améliorer l'expérience utilisateur (puisque l'on pouvait voir les images en défilant), mais ce n'est pas avantageux car la fluidité du défilement peut en souffrir énormément. Dans cet exemple, NSURLConnectionune boucle d'exécution ne doit pas être utilisée; il serait préférable d'utiliser les UIScrollViewméthodes déléguées pour détecter la fin du défilement, puis mettre à jour le tableau et télécharger de nouveaux éléments à partir du réseau;

  • Vous pouvez envisager d'utiliser GCD qui vous aidera à «protéger» votre code des problèmes de gestion de la boucle d'exécution. Dans l'exemple ci-dessus, vous pouvez envisager d'ajouter vos demandes réseau à une file d'attente série personnalisée.

viggio24
la source
9
Cher Viggio24, merci beaucoup pour cette explication claire et précise. Je demanderais à Apple d'inclure votre commentaire dans son guide API. ;)
Stkim1
7
La réponse de viggio24 est parfaite. Pour ceux qui sont intéressés, je tiens à souligner que la Session 208 (Network Apps for iPhone OS, Part 2) de la WWDC 2010 contient une introduction sur les boucles d'exécution. Si vous êtes intéressé, jetez un œil. J'espère que ça aide.
Lorenzo B
19
Juste une note pour moi: NSRunLoopCommonModesautorise l'événement du minuteur lors du défilement UIScrollView. NSDefaultRunLoopModeempêcher la minuterie pendant le défilement.
eonil du
2
J'ai trouvé très intéressant le commentaire sur la mise à jour de la vue de défilement, car il mentionne un sujet très difficile. Juste pour ajouter plus de détails à ce sujet: lorsque vous définissez un mode pour une NSURLConnection, cela n'affecte que l'exécution des rappels délégués. Je comprends que la mise à jour de scrollView ici pourrait entraîner un problème de performances, mais pourquoi cela se produit-il? Si la réponse est que l'image doit être chargée en mémoire, vous pouvez le faire en écrivant dans un contexte graphique en arrière-plan et mettre à jour votre couche principale de thread de vue après cela. cela semble-t-il raisonnable?
nebillo