Sobes.tech
Middle

Защо не се препоръчва да се натоварва главният поток в iOS и какви са начините да се избегне това?

sobes.tech AI

Отговор от AI

Главният поток отговаря за актуализирането на потребителския интерфейс и обработката на събитията на UI. Всяка дълга операция (мрежови заявки, тежки изчисления, работа с файлове) в главния поток ще го блокира, което ще доведе до "замръзнал" UI, ниска отзивчивост на приложението и дори негово сриване (watchdog timeout).

Начини за избягване на натоварване на главния поток:

  1. Grand Central Dispatch (GCD): Позволява изпълнение на задачи асинхронно във фонови опашки.

    • DispatchQueue.global(): Получаване на глобални фонови опашки с различен приоритет.
    • DispatchQueue.main: Главната опашка за актуализиране на UI.
    • async { ... }: Изпълнение на блок код асинхронно.
    // изпълнение на тежка задача във фонов поток
    DispatchQueue.global(qos: .userInitiated).async {
        // тежки изчисления или мрежова заявка
    
        // актуализиране на UI на главния поток
        DispatchQueue.main.async {
            // актуализиране на потребителския интерфейс
        }
    }
    
  2. Operation Queues (Operation & OperationQueue): По-високо ниво на абстракция над GCD опашките, поддържаща зависимости между операции, анулиране и пауза.

    • OperationQueue(): Създаване на опашка за операции.
    • Operation: Абстрактен клас за задачи, които могат да бъдат добавени към опашката.
    • addOperation(_:): Добавяне на операция към опашката.
    let downloadOperation = BlockOperation {
        // изтегляне на данни
    }
    
    let parseOperation = BlockOperation {
        // разбиране на данните
    }
    
    // зависимост: разборът започва след изтеглянето
    parseOperation.addDependency(downloadOperation)
    
    let queue = OperationQueue()
    queue.addOperation(downloadOperation)
    queue.addOperation(parseOperation)
    
  3. Concurrency (async/await): Модерен подход в Swift за асинхронно програмиране.

    • async: Обозначава функция като асинхронна.
    • await: Изчаква завършването на асинхронна операция.
    • Task: Създаване на нова конкурентна задача.
    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()
                // обработка на данните
                // автоматично обновяване на UI на главния поток
                // (ако Task се създава в главния Actor или DispatchQueue.main)
            } catch {
                // обработка на грешка
            }
        }
    }
    
  4. Actors: Предотвратяват гонки на данни при достъп от различни конкурентни контексти, осигурявайки ексклузивен достъп до състоянието си.

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

Изборът между GCD, Operation Queues и Concurrency зависи от сложността на задачата, необходимото ниво на контрол и изискванията към структурата на кода. Concurrency е предпочитаният подход в съвременните приложения поради четливост и безопасност.