В чем разница между run loop и dispatch queue
Что проверяют
Различаете ли вы механизм ожидания событий на конкретном потоке и механизм планирования задач по пулу потоков. Типичное уточнение: почему Timer не срабатывает во время скролла или на фоновой очереди.
Как отвечать
- run loop: цикл обработки событий, привязанный к потоку. У каждого потока не больше одного run loop, создаётся он лениво при обращении к
RunLoop.current. Главный цикл запускаетUIApplicationMain, на остальных потоках его никто не крутит, пока вы сами не вызоветеrun(). Он обслуживает источники ввода и таймеры, а когда событий нет, усыпляет поток без траты CPU; - режимы run loop: цикл работает в одном режиме за раз. Пока пользователь скроллит, главный поток находится в
.tracking, и таймер из.defaultмолчит. Решение: добавить таймер в.common; - dispatch queue: очередь блоков в GCD, последовательная или конкурентная. Потоками управляет система, привязки задачи к конкретному потоку нет, кроме
DispatchQueue.main; - связь между ними: блоки главной очереди выполняет главный поток в рамках итераций своего run loop.
Практический пример отличия: в Combine receive(on: RunLoop.main) задерживает события, пока идёт скролл, а receive(on: DispatchQueue.main) нет. В Swift Concurrency @MainActor тоже исполняется на главной очереди.
Итог в одну фразу: run loop отвечает на вопрос «как потоку ждать событий», очередь отвечает на вопрос «где и в каком порядке выполнить работу».
Где спотыкаются
Отождествляют очередь с потоком. Две задачи одной последовательной очереди могут выполниться на разных потоках, гарантирован только порядок.
Создают Timer.scheduledTimer внутри DispatchQueue.global().async и удивляются, что он не срабатывает: у потока из пула нет запущенного run loop.
Вызывают DispatchQueue.main.sync с главного потока. Это гарантированный deadlock: главная очередь ждёт сама себя.