> Как работает таймер в iOS и с чем связана его работа (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Совкомбанк
Стек: iOS, Swift
> Пример ответа
Короткий ответ
Таймер в iOS - это объект Timer, который планирует выполнение блока или метода через заданный интервал. Его работа связана с run loop: таймер регистрируется в определённом режиме (default, common, tracking) и срабатывает, когда run loop обрабатывает события. Важно: таймер не гарантирует точное время при высокой нагрузке на главный поток, так как он зависит от доступности run loop. Для фоновых задач используют DispatchSourceTimer или Task.sleep.
Подробное объяснение
Timer в Foundation - это обёртка над CFRunLoopTimer. При создании таймер добавляется в текущий run loop (обычно главный) в указанном режиме. Run loop проверяет таймеры на каждой итерации: если fireDate наступил и режим активен, вызывается обработчик.
Ключевые аспекты:
- Режимы run loop:
default- обычная работа,tracking- во время скролла,common- объединяет несколько режимов. Если таймер добавлен вdefault, он не сработает во время отслеживания жестов (например, при скролле), если явно не добавить его вcommon. - Задержки: таймер срабатывает не точно в момент
fireDate, а при ближайшей итерации run loop. Если главный поток занят длительной синхронной задачей, таймер "опаздывает" и может пропустить несколько срабатываний (для повторяющихся таймеров). - Retain cycle: run loop удерживает таймер, таймер удерживает target (если используется selector-based API). Поэтому нужно вызывать
invalidate()для остановки и освобождения. - Альтернативы:
DispatchSourceTimer- работает на GCD, не зависит от run loop, точнее и эффективнее для высокочастотных задач.Task.sleepв async/await - для современных асинхронных сценариев, не блокирует поток.CADisplayLink- для синхронизации с частотой обновления экрана (анимации).
На практике
- Для UI-обновлений используй
Timerна главном потоке с режимом.common, чтобы таймер работал во время скролла. - Для фоновых периодических задач (например, обновление данных) предпочтительнее
DispatchSourceTimer- он не зависит от run loop и работает в фоновой очереди. - Всегда сохраняй ссылку на таймер и вызывай
invalidate()вdeinitили при уходе с экрана, иначе будет утечка памяти. - Для точных интервалов (например, игровой цикл) используй
CADisplayLinkилиDispatchSourceTimerс учётом времени последнего срабатывания, так какTimerможет "дрейфовать".
Пример кода
SWIFTfinal class TimerExample {private var timer: Timer?func startTimer() {// Режим .common - работает во время скроллаtimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer inself?.updateUI()}// Добавляем в common режим, если создавали через timerWithTimeIntervalRunLoop.main.add(timer!, forMode: .common)}func stopTimer() {timer?.invalidate()timer = nil}private func updateUI() {// обновление интерфейса}deinit {timer?.invalidate()}}// Альтернатива на GCDfinal class DispatchTimerExample {private var timer: DispatchSourceTimer?func startTimer() {let queue = DispatchQueue.global(qos: .background)timer = DispatchSource.makeTimerSource(queue: queue)timer?.schedule(deadline: .now(), repeating: 1.0, leeway: .milliseconds(100))timer?.setEventHandler { [weak self] inself?.doWork()}timer?.resume()}func stopTimer() {timer?.cancel()timer = nil}}
Как отвечать на собеседовании
Начни с определения: Timer - это обёртка над CFRunLoopTimer, работает через run loop. Затем объясни связь с run loop: таймер срабатывает только когда run loop активен и находится в нужном режиме. Упомяни, что точность не гарантируется при блокировке главного потока. После этого перечисли альтернативы и их преимущества: DispatchSourceTimer для фоновых задач, CADisplayLink для анимаций, Task.sleep для async/await. Если спросят про утечки - объясни retain cycle и необходимость invalidate(). Для senior-уровня добавь рассуждение о том, когда какой инструмент выбирать: Timer для простых UI-задач, GCD-таймер для критичных по времени операций.
Что проверяет интервьюер
- Понимание устройства run loop и его режимов.
- Осознание ограничений
Timer(неточность, зависимость от главного потока). - Знание альтернатив и умение обосновать выбор.
- Внимание к управлению памятью (retain cycle, invalidate).
- Понимание разницы между
TimerиDispatchSourceTimerв контексте потоков и планирования.
Типичные ошибки
- Утверждение, что
Timerработает точно по времени - это не так, он зависит от run loop. - Забывают про режим
.common, из-за чего таймер не срабатывает во время скролла. - Не вызывают
invalidate(), что приводит к утечке памяти. - Используют
Timerв фоновых потоках без явного добавления в run loop этого потока - таймер не сработает. - Путают
TimerсDispatchSourceTimer, считая их взаимозаменяемыми без учёта контекста. - Не учитывают, что повторяющийся
Timerможет "схлопываться" при задержках - он пропускает пропущенные срабатывания, а не нагоняет их.
> Похожие задачи по mobile
Какие возможности есть у bloc из коробки во Flutter
Какие пакеты для проверки состояния сети во Flutter используются
Что происходит с таймером, если он запущен в глобальной очереди, а не в главной в iOS
Что происходит при дедлоке в главной очереди в iOS
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью