Как взаимодействуют RunLoop и DispatchQueue (GCD) в iOS?

«Как взаимодействуют RunLoop и DispatchQueue (GCD) в iOS?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

RunLoop и GCD (DispatchQueue) — это два независимых, но взаимодействующих механизма управления задачами в потоке.

RunLoop — это цикл событий, связанный с конкретным потоком. Он планирует работу и обрабатывает входные источники (источники событий, таймеры).

GCD (Grand Central Dispatch) — это библиотека для параллельного выполнения задач на основе очередей, абстрагирующаяся от потоков.

Взаимодействие:

  1. Главный поток (Main Thread): Имеет главный RunLoop (RunLoop.main). Очередь DispatchQueue.main добавляет свои блоки (tasks) в качестве источников событий в этот главный RunLoop. Именно RunLoop главного потока выполняет эти блоки.
    // Этот блок будет передан в Main RunLoop и выполнен в его цикле
    DispatchQueue.main.async {
        self.label.text = "Updated"
    }
  2. Фоновые потоки: По умолчанию у фоновых потоков, созданных GCD, нет активного RunLoop. RunLoop нужно создавать и запускать явно (RunLoop.current.run()), что требуется редко (например, для Timer в фоновом потоке).
    DispatchQueue.global().async {
        // Таймеру нужен активный RunLoop
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            print("Tick")
        }
        // Запускаем RunLoop вручную. Поток будет занят этим циклом.
        RunLoop.current.run(mode: .default, before: .distantFuture)
    }

Вывод:

  • GCD — предпочтительный способ для большинства асинхронных и параллельных задач.
  • RunLoop — низкоуровневый механизм, который DispatchQueue.main использует для интеграции с главным потоком. Прямое использование RunLoop необходимо для работы с таймерами в фоновых потоках, обработки пользовательских источников событий или сокетов в режиме ожидания.