Как работает таймер в iOS и с чем связана его работа
Что проверяют
Знаете ли вы, что Timer сам ничего не отсчитывает, а живёт внутри run loop потока, и отсюда все его особенности. Следом обычно спрашивают, почему таймер замирает при скролле и откуда берётся утечка контроллера.
Как отвечать
Главная мысль: Timer это источник событий для RunLoop. scheduledTimer(...) создаёт таймер и добавляет его в run loop текущего потока в режиме .default. Run loop на каждом витке проверяет, наступило ли время срабатывания, и вызывает блок или селектор.
- режимы: пока пользователь скроллит
UIScrollView, главный run loop работает в режиме.tracking, и таймер из.defaultне срабатывает. Решение:RunLoop.main.add(timer, forMode: .common); - поток: на фоновом потоке таймер сработает, только если там запущен run loop. На очереди GCD его нет, и таймер просто молчит, для очередей есть
DispatchSourceTimer; - точность: срабатывание не раньше заданного времени, но может опоздать, если поток занят. Пропущенные тики не догоняются. Свойство
toleranceразрешает системе сдвигать срабатывания и экономить батарею; - память: run loop держит таймер, а таймер сильно держит
target. Контроллер не освободится, пока таймер не остановлен. Используйте блочный API с[weak self]и вызывайтеinvalidate()на том же потоке, где таймер добавлен.
Смежные инструменты: CADisplayLink для анимаций, привязанных к частоте экрана (на ProMotion до 120 Гц), Timer.publish(every:on:in:) в Combine и Task.sleep в Swift Concurrency. Когда приложение уходит в фон и приостанавливается, таймеры не работают вовсе, для отложенных событий нужны локальные уведомления или фоновые задачи.
Где спотыкаются
Говорят, что таймер всегда работает на главном потоке. Он работает на потоке того run loop, куда добавлен.
Ставят invalidate() в deinit при таймере с target: self и не понимают, почему deinit не вызывается: цикл удержания замыкается через run loop.
Делают обратный отсчёт, вычитая секунду на каждый тик. После скролла или ухода в фон отсчёт уплывает, правильно хранить момент окончания и считать остаток от текущего времени.