Ответ
RunLoop (Цикл выполнения) — это объект инфраструктуры, который управляет обработкой входящих событий для потока (Thread) и планирует выполнение задач. Он работает в цикле, ожидая события и передавая их на обработку.
Основные задачи RunLoop:
- Управление событиями: Принимает и распределяет события из различных источников (source):
- Input Sources: События от портов (Port), кастомные источники, системные события (тапы, жесты).
- Timer Sources: События от таймеров (
Timer).
- Планирование задач: Выполняет блоки кода, добавленные в очередь (
perform(_:on:with:waitUntilDone:)). - Энергоэффективность: Когда нет событий для обработки, 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 без источников событий) может привести к "зависанию" потока.