3 votes

Swift : Mise à jour de l'interface utilisateur - Fonction entière sur le fil principal ou seulement la mise à jour de l'interface utilisateur ?

J'ai lu que l'interface utilisateur doit toujours être mise à jour sur le thread principal. Cependant, je suis un peu perdu lorsqu'il s'agit de la méthode à privilégier pour mettre en œuvre ces mises à jour.

J'ai plusieurs fonctions qui effectuent des contrôles conditionnels, puis le résultat est utilisé pour déterminer comment mettre à jour l'interface utilisateur. Ma question est la suivante : la fonction entière doit-elle être exécutée sur le thread principal ? Est-ce que seule l'interface utilisateur doit être mise à jour ? Puis-je / dois-je exécuter les contrôles conditionnels sur un autre thread ? Cela dépend-il de ce que fait la fonction ou de la vitesse à laquelle elle doit être exécutée ?

Exemple d'une fonction qui change l'image à l'intérieur d'un ImageView sans threading :

@IBAction func undoPressed(_ sender: Any) {
        if !previousDrawings.isEmpty {
            previousDrawings.remove(at: previousDrawings.count - 1)
            if let lastDrawing = previousDrawings.last {
                topImageView.image = lastDrawing
            }
            else {
                // empty
                topImageView.image = nil
            }
        }
    }

Devrais-je mettre topImageView.image sur le fil principal ? Comme ceci :

 @IBAction func undoPressed(_ sender: Any) {
        if !previousDrawings.isEmpty {
            previousDrawings.remove(at: previousDrawings.count - 1)
            if let lastDrawing = previousDrawings.last {
                DispatchQueue.main.async {
                    self.topImageView.image = lastDrawing
                }
            }
            else {
                DispatchQueue.main.async {
                    self.topImageView.image = nil
                }
            }
        }
    }

Devrais-je utiliser un fil de fond pour les contrôles conditionnels ? Comme ceci :

@IBAction func undoPressed(_ sender: Any) {
        DispatchQueue.global(qos: .utility).async {
            if !previousDrawings.isEmpty {
                previousDrawings.remove(at: previousDrawings.count - 1)
                if let lastDrawing = previousDrawings.last {
                    DispatchQueue.main.async {
                        self.topImageView.image = lastDrawing
                    }
                }
                else {
                    DispatchQueue.main.async {
                        self.topImageView.image = nil
                    }
                }
            }
        }
    }

Si quelqu'un pouvait expliquer quelle méthode est préférée et pourquoi, ce serait vraiment utile.

3voto

Duncan C Points 18661

Back up. Sauf dans des circonstances particulières, todos votre code est exécuté sur le thread principal. UIAction par exemple, sont TOUJOURS exécutées sur le fil d'exécution principal, comme le sont toutes les méthodes définies par l'option UIViewController et ses différentes sous-classes. En fait, vous pouvez dire sans risque que les méthodes UIKit sont exécutées sur le thread principal. Encore une fois, vos méthodes ne seront appelées sur un thread d'arrière-plan que dans des circonstances très particulières, qui sont bien documentées.

Vous pouvez utiliser GCD pour exécuter des blocs de code sur des threads d'arrière-plan. Dans ce cas, le code est exécuté sur un thread d'arrière-plan parce que vous avez explicitement demandé que cela se produise.

Certaines fonctions du système (comme URLSession ) appellent leurs méthodes déléguées/exécutent leurs gestionnaires d'achèvement sur des threads d'arrière-plan par défaut. Ceux-ci sont bien documentés. Pour les bibliothèques tierces comme AlamoFire ou FireBase, vous devrez lire la documentation, mais tout code qui est appelé sur un thread d'arrière-plan doit être très bien documenté car vous devez prendre des précautions spéciales pour le code qui s'exécute sur un thread d'arrière-plan.

La raison habituelle d'utiliser un thread d'arrière-plan est qu'une tâche de longue durée peut être exécutée jusqu'à son terme sans geler l'interface utilisateur jusqu'à ce qu'elle soit terminée.

Un modèle courant, par exemple, consiste à utiliser URLSession pour lire des JSON des données provenant d'un serveur distant. Le gestionnaire d'achèvement est appelé sur un thread d'arrière-plan car l'analyse des données que vous recevez peut prendre du temps. Cependant, une fois l'analyse terminée, l'appel à la mise à jour de l'interface utilisateur est enveloppé dans un appel GCD au thread principal, car les modifications de l'interface utilisateur doivent être effectuées sur le thread principal.

2voto

rmaddy Points 79279

Tout d'abord, votre undoPressed sera appelée sur la file d'attente principale.

Dans le premier jeu de code, tout sera sur la file d'attente principale.

Dans le deuxième ensemble de code, en utilisant DispatchQueue.main.async est inutile puisque le reste du code est déjà dans la file principale.

Donc, en réalité, vos deux seules options raisonnables sont 1 et 3.

Compte tenu de votre code, l'option 1 est parfaite. Vous ne voudriez utiliser l'option 3 que si le code exécuté en arrière-plan prenait plus qu'une quantité triviale de temps pour s'exécuter. Puisque le code que vous avez ici est trivial et ne prendra pratiquement pas de temps à exécuter, l'option 3 n'a aucun intérêt ici.

Il suffit donc d'utiliser votre premier jeu de codes et tout ira bien.

Ne vous souciez pas de déplacer le code en arrière-plan lorsqu'il doit exécuter une grande boucle, calculer un algorithme compliqué ou effectuer toute sorte d'accès au réseau.

2voto

Prashant Gaikwad Points 291

Swift 4 :

 DispatchQueue.main.async {
          //Your code
     }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X