Почему обновление UI должно выполняться в main queue?

«Почему обновление UI должно выполняться в main queue?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Потому что UIKit и SwiftUI не являются потокобезопасными. Все операции с пользовательским интерфейсом должны выполняться на главном потоке (main thread). Нарушение этого правила приводит к:

  • Неожиданным крашам (часто с ошибкой Main Thread Checker).
  • Визуальным артефактам и задержкам отрисовки.
  • Непредсказуемому поведению элементов интерфейса.

Правильный паттерн:

// 1. Выполняем тяжелую работу в фоне
DispatchQueue.global(qos: .userInitiated).async {
    let processedImage = self.applyFilters(to: originalImage)

    // 2. Возвращаемся на главный поток для обновления UI
    DispatchQueue.main.async {
        self.imageView.image = processedImage
        self.activityIndicator.stopAnimating()
    }
}

SwiftUI (автоматическое управление):

Task {
    // Автоматически выполняется в фоновом потоке
    let data = await fetchData()

    // Обновление @Published/@State свойств автоматически
    // переносится на главный поток
    self.items = data
}

Исключения (но с осторожностью):

  • Создание UIImage(data:) из фонового потока допустимо, но установка в UIImageView — только на главном.
  • Некоторые вычисления layout (например, sizeThatFits) могут работать в фоне, если не обращаются к UI-иерархии.

Проверка текущего потока:

assert(Thread.isMainThread, "Этот код должен выполняться на главном потоке!")