Зачем нужна отдельная очередь для выполнения запросов в iOS
Что проверяют
Понимаете ли вы, что главный поток занят интерфейсом, и что именно нужно уносить с него при работе с сетью. Сильный ответ отделяет сам запрос, который URLSession и так выполняет асинхронно, от обработки ответа и синхронизации общего состояния. Следом спрашивают, как это выглядит на Swift Concurrency.
Как отвечать
Главная очередь рисует интерфейс и обрабатывает касания. На экране 60 или 120 Гц на кадр остаётся около 16 или 8 мс, и любая синхронная работа на ней превращается в подвисания.
Важная деталь: запрос через URLSession сам главный поток не блокирует. Колбэки приходят на delegateQueue сессии, а если её не задали, сессия создаёт свою последовательную OperationQueue. Отдельная очередь нужна для другого:
- обработка ответа: декодирование большого JSON через
JSONDecoder, картинок, запись в Core Data или SwiftData. Это делается в фоне, на главную очередь уходит готовая модель; - синхронизация: последовательная очередь (или актор) защищает общее состояние, например обновление токена, чтобы десять запросов с истёкшим токеном не запустили десять обновлений;
- управление задачами:
OperationQueueсmaxConcurrentOperationCount, зависимостями и отменой, приоритеты через QoS (.userInitiated,.utility,.background).
Результат в интерфейс отдают через DispatchQueue.main.async или @MainActor. На Swift Concurrency идея та же: try await URLSession.shared.data(from: url) не держит поток, общее состояние живёт в actor, а view model помечена @MainActor.
Где спотыкаются
Обновляют UI прямо из колбэка dataTask. Он приходит не на главной очереди, Main Thread Checker это ловит, а в релизе получаются редкие падения и артефакты.
Вызывают синхронный Data(contentsOf:) внутри DispatchQueue.global().async. Поток пула блокируется на всё время ответа, при сотне запросов GCD плодит потоки, и приложение тормозит уже из-за них.
Вызывают DispatchQueue.main.sync с главного потока. Это дедлок, который libdispatch обрывает падением приложения.