Что происходит с таймером, если он запущен в глобальной очереди, а не в главной в iOS
Что проверяют
Знаете ли вы, что Timer это объект цикла событий, а не самостоятельная сущность, и что у потоков из пула GCD цикла событий нет. Заодно смотрят, отличаете ли вы «таймер не сработает» от «таймер сработает не на главном потоке».
Как отвечать
Ключ в том, что Timer требует работающий RunLoop. Метод Timer.scheduledTimer добавляет таймер в RunLoop.current того потока, где его создали. У главного потока цикл крутится всегда, у потока из DispatchQueue.global() его никто не запускает, поэтому таймер просто не выстрелит ни разу.
Варианты выхода:
- создавать
Timerна главном потоке, а тяжёлую работу из колбэка уводить на фоновую очередь; - брать
DispatchSource.makeTimerSource(queue:): ему цикл событий не нужен, он живёт на очереди. Ссылку на источник надо держать, иначе он освободится и перестанет тикать; - в структурированной конкурентности ту же роль играет задача с
Task.sleepв цикле и проверкой отмены.
Добавьте про режимы: таймер, добавленный в .default, замолкает на время скролла, потому что цикл переключается в режим отслеживания, и лечится это RunLoop.main.add(timer, forMode: .common). И про точность: момент срабатывания не гарантирован, таймер ждёт освобождения потока, поэтому для анимации берут CADisplayLink, а интервалы считают по часам, а не накоплением шагов.
Где спотыкаются
Отвечают, что таймер на глобальной очереди работает в фоне и нельзя из него только трогать интерфейс. Он не работает вовсе, а про интерфейс говорят по привычке. Правильный ответ звучит иначе: сработает он лишь тогда, когда на этом потоке кто-то запустит цикл событий и займёт поток целиком.
Цикл удержания. Таймер держит сильную ссылку на цель, и контроллер не освобождается, пока таймер не остановлен вызовом invalidate(). Звать его нужно на том же потоке, где таймер добавлен в цикл.
Ждут, что таймер продолжит тикать, когда приложение ушло в фон. Цикл событий останавливается, таймеры замирают и пропущенные срабатывания не догоняют. Для отложенных действий нужны локальные уведомления или фоновые задачи, а после возвращения прошедшее время считают по дате, а не по числу тиков.