Sobes.tech
Middle

Pourquoi n'est-il pas recommandé de charger le thread principal sur iOS et quelles sont les méthodes pour l'éviter?

sobes.tech IA

Réponse de l'IA

Le thread principal est responsable de la mise à jour de l'interface utilisateur et de la gestion des événements UI. Toute opération longue (requêtes réseau, calculs intensifs, travail avec des fichiers) sur le thread principal le bloquera, ce qui entraînera une interface "gelée", une faible réactivité de l'application et même son crash (timeout du watchdog).

Comment éviter la surcharge du thread principal :

  1. Grand Central Dispatch (GCD) : Permet d'exécuter des tâches de manière asynchrone sur des files d'attente en arrière-plan.

    • DispatchQueue.global(): Obtient des files d'attente globales en arrière-plan avec différentes priorités.
    • DispatchQueue.main: File d'attente principale pour la mise à jour de l'UI.
    • async { ... }: Exécute un bloc de code de manière asynchrone.
    // exécuter une tâche lourde en arrière-plan
    DispatchQueue.global(qos: .userInitiated).async {
        // calculs lourds ou requête réseau
    
        // mise à jour de l'UI sur le thread principal
        DispatchQueue.main.async {
            // mettre à jour l'interface utilisateur
        }
    }
    
  2. Queues d'Opérations (Operation & OperationQueue) : Abstraction de haut niveau sur les queues GCD, supportant dépendances, annulation et suspension.

    • OperationQueue(): Création d'une queue d'opérations.
    • Operation: Classe abstraite pour les tâches pouvant être ajoutées à la queue.
    • addOperation(_:): Ajout d'une opération à la queue.
    let downloadOperation = BlockOperation {
        // télécharger des données
    }
    
    let parseOperation = BlockOperation {
        // analyser des données
    }
    
    // dépendance : l'analyse commence après le téléchargement
    parseOperation.addDependency(downloadOperation)
    
    let queue = OperationQueue()
    queue.addOperation(downloadOperation)
    queue.addOperation(parseOperation)
    
  3. Concurrence (async/await) : Approche moderne en Swift pour la programmation asynchrone.

    • async: Marque une fonction comme asynchrone.
    • await: Attend la fin d'une opération asynchrone.
    • Task: Crée une nouvelle tâche concurrente.
    func fetchData() async throws -> Data {
        let url = URL(string: "https://example.com/data")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return data
    }
    
    func updateUI() {
        Task {
            do {
                let data = try await fetchData()
                // traiter les données
                // mettre à jour l'UI sur le thread principal automatiquement
                // (si la tâche est créée dans un Actor principal ou DispatchQueue.main)
            } catch {
                // gérer l'erreur
            }
        }
    }
    
  4. Acteurs : Empêchent les conditions de course lors de l'accès depuis différents contextes concurrents, en assurant un accès exclusif à leur état.

    actor DataStore {
        private var data: [String] = []
    
        func addData(_ item: String) {
            data.append(item)
        }
    
        func getData() -> [String] {
            return data
        }
    }
    
    let store = DataStore()
    
    Task {
        await store.addData("item1")
    }
    

Le choix entre GCD, Queues d'Opérations et Concurrence dépend de la complexité de la tâche, du niveau de contrôle nécessaire et des exigences de structuration du code. La concurrence est l'approche privilégiée dans les applications modernes pour sa lisibilité et sa sécurité.