Что такое RunLoop в iOS/macOS и какова его основная задача?

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

Ответ

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

Основные задачи RunLoop:

  1. Управление событиями: Принимает и распределяет события из различных источников (source):
    • Input Sources: События от портов (Port), кастомные источники, системные события (тапы, жесты).
    • Timer Sources: События от таймеров (Timer).
  2. Планирование задач: Выполняет блоки кода, добавленные в очередь (perform(_:on:with:waitUntilDone:)).
  3. Энергоэффективность: Когда нет событий для обработки, RunLoop переводит свой поток в спящий режим, экономя ресурсы CPU.

Пример работы с Timer и RunLoop:

// Создаем таймер
let timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
    print("Таймер сработал на RunLoop: (RunLoop.current)")
}
// Таймер автоматически добавляется в текущий RunLoop в режиме `.default`.

// Чтобы таймер работал во время прокрутки (в iOS),
// его нужно добавить в общий режим (`.common`).
RunLoop.current.add(timer, forMode: .common)

Ключевые режимы (Mode) RunLoop:

  • .default: Режим по умолчанию для большинства операций.
  • .common: Набор режимов, который включает .default и .tracking (режим отслеживания жестов). Объекты, добавленные в .common, работают во всех этих режимах.
  • .tracking: Активен во время отслеживания пользовательского взаимодействия (например, прокрутки UIScrollView).

Важные нюансы:

  • Главный поток (Main Thread) имеет всегда активный RunLoop, который управляет всем UI.
  • Фоновые потоки по умолчанию не имеют запущенного RunLoop. Чтобы он работал, нужно запустить его вручную (RunLoop.current.run()), но обычно в этом нет необходимости для стандартных операций.
  • Таймеры (Timer) планируются на конкретном RunLoop. Если этот RunLoop занят (например, блокирующей операцией), таймер не сработает вовремя.
  • Неправильное использование (например, запуск RunLoop без источников событий) может привести к "зависанию" потока.