Какие методы есть для выполнения тяжеловесных задач асинхронно без блокировки интерфейса
Что проверяют
Понимаете ли вы, где на самом деле выполняется код, а не только знаете названия API. Следом обычно спрашивают, почему экран всё равно подвисает, хотя вызов обёрнут в Task, и как отменить работу, когда пользователь ушёл с экрана.
Как отвечать
Правило одно: главный поток занят только интерфейсом, тяжёлое уходит в фон, результат возвращается на главный поток. Инструменты:
- Swift Concurrency: основной способ в новом коде.
async/await,TaskGroupдля параллельных подзадач,actorдля защиты состояния,@MainActorдля кода с UI. Отмена кооперативная: задача сама проверяетTask.isCancelledили вызываетtry Task.checkCancellation(); - GCD:
DispatchQueue.global(qos: .userInitiated).asyncдля работы иDispatchQueue.main.asyncдля обновления UI. Встречается в старом коде и в обёртках над C-библиотеками; - OperationQueue: зависимости между операциями,
maxConcurrentOperationCount, отмена черезcancel(). Удобно для цепочек вроде «скачать, обработать, сохранить»; - системные API:
URLSessionсasync-методами,BGTaskSchedulerдля работы, которая должна пережить сворачивание.
Важная деталь: Task { }, созданный из контекста @MainActor, наследует главный актор, и синхронный цикл внутри него заморозит интерфейс. Тяжёлый расчёт выносят в nonisolated функцию, отдельный актор или Task.detached. В Swift 6.2 с режимом approachable concurrency nonisolated async функции по умолчанию остаются на акторе вызывающего, и для ухода в фон их помечают @concurrent.
Добавьте выбор QoS: .userInitiated для того, чего пользователь ждёт, .utility для долгих операций с прогрессом, .background для невидимой работы.
Где спотыкаются
Считают, что await сам переносит работу в фон. Он лишь отмечает точку приостановки, а где выполнится код, решает изоляция функции.
Запускают работу, которую пользователь ждёт, с QoS .background. Система может надолго её отложить, особенно в режиме энергосбережения.
Обновляют UILabel или @Published свойство из фоновой очереди. Main Thread Checker и строгий режим Swift 6 это ловят, а в старом коде такие ошибки дают редкие падения.