Sobes.tech
Middle

Perché non è consigliabile sovraccaricare il thread principale in iOS e quali sono i modi per evitarlo?

sobes.tech AI

Risposta dell'AI

Il thread principale si occupa di aggiornare l'interfaccia utente e di gestire gli eventi UI. Qualsiasi operazione lunga (richieste di rete, calcoli intensivi, lavoro con file) sul thread principale lo bloccherà, portando a un'interfaccia "congelata", bassa reattività dell'applicazione e persino al suo crash (timeout del watchdog).

Modi per evitare il carico sul thread principale:

  1. Grand Central Dispatch (GCD): Permette di eseguire task in modo asincrono su code di background.

    • DispatchQueue.global(): Accesso a code di background globali con priorità diverse.
    • DispatchQueue.main: Coda principale per aggiornare l'UI.
    • async { ... }: Esegue un blocco di codice in modo asincrono.
    // eseguire un task pesante in background
    DispatchQueue.global(qos: .userInitiated).async {
        // calcoli pesanti o richiesta di rete
    
        // aggiornare l'UI sul thread principale
        DispatchQueue.main.async {
            // aggiornare l'interfaccia utente
        }
    }
    
  2. Code di Operazioni (Operation & OperationQueue): Astrazione di livello superiore sulle code GCD, supporta dipendenze tra operazioni, cancellazioni e sospensioni.

    • OperationQueue(): Creazione di una coda di operazioni.
    • Operation: Classe astratta per task che possono essere aggiunti alla coda.
    • addOperation(_:): Aggiunta di un'operazione alla coda.
    let downloadOperation = BlockOperation {
        // scaricare dati
    }
    
    let parseOperation = BlockOperation {
        // analizzare dati
    }
    
    // dipendenza: l'analisi inizia dopo il download
    parseOperation.addDependency(downloadOperation)
    
    let queue = OperationQueue()
    queue.addOperation(downloadOperation)
    queue.addOperation(parseOperation)
    
  3. Concorrenza (async/await): Approccio moderno in Swift per la programmazione asincrona.

    • async: Marca una funzione come asincrona.
    • await: Attende il completamento di un'operazione asincrona.
    • Task: Crea un nuovo task concorrente.
    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()
                // processare dati
                // aggiornare UI sul thread principale automaticamente
                // (se il task viene creato nell'Actor principale o DispatchQueue.main)
            } catch {
                // gestire errore
            }
        }
    }
    
  4. Attori: Prevengono condizioni di race durante l'accesso da diversi contesti concorrenti, garantendo accesso esclusivo al loro stato.

    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")
    }
    

La scelta tra GCD, Code di Operazioni e Concorrenza dipende dalla complessità del compito, dal livello di controllo necessario e dai requisiti di strutturazione del codice. La concorrenza è l'approccio preferito nelle applicazioni moderne per la sua leggibilità e sicurezza.