Как вызвать функцию Swift в указанную дату/время

Каков наилучший способ запуска функции/блока с точной (в пределах 1 секунды) датой/временем в Swift на iOS?

Я пытался использовать Timerinit с fireAt, но обнаружил, что точность часто снижается на несколько секунд. Установка точности таймера на значение, подобное 0,5, не дала лучших результатов.

Я также пытался использовать GCD с DispatchQueue.main.asyncAfter(.now() + someDelay), и в моем тестировании он также часто отличался на несколько секунд.

Я остановился на создании таймера, который срабатывает раз в секунду, а затем я проверяю, истекла ли текущая дата/время желаемого значения, а затем вручную запускаю свой код. Кажется, что может быть лучший способ сделать это, но у меня нет идей. Спасибо!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
36
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы создадите таймер GCD с makeTimerSource с флагом .strict, он откажется от объединения таймеров:

The system makes its best effort to observe the timer’s specified leeway value, even if the value is smaller than the default leeway.

Например:

var timer: DispatchSourceTimer?

func startTimer(in interval: DispatchTimeInterval, block: @escaping () -> Void) {
    timer = DispatchSource.makeTimerSource(flags: .strict, queue: .main)
    timer?.setEventHandler(handler: block)
    timer?.schedule(deadline: .now() + interval, leeway: .milliseconds(500))
    timer?.activate()
}

А потом:

startTimer(in: .seconds(240)) { [weak self] in
    self?.foo()
}

Обратите внимание, что в отличие от Timer, который сохраняется за RunLoop, на который вы его запланировали, вы должны сохранить собственную сильную ссылку на таймер GCD, как показано выше. Вы можете создать Timer свойства weak (чтобы они освобождались, когда RunLoop освобождает их), но вам нужны сильные ссылки на таймеры GCD.

Несколько предостережений:

  • Обратите внимание на использование списка захвата [weak self]. Как правило, вы хотите избежать сильного эталонного цикла (особенно если таймер был повторяющимся таймером).

  • Этот шаблон следует использовать только в случае крайней необходимости, поскольку вы отказываетесь от функций энергосбережения, предлагаемых объединением таймеров. Я ожидаю, что это более энергоэффективно, чем подход с повторяющимся таймером, но менее энергоэффективно, чем позволить ОС использовать свое усмотрение для объединения своих таймеров.

  • Этот шаблон предполагает, что очередь, для которой он запланирован, не заблокирована. Очевидно, что мы никогда не блокируем основной поток, но вы всегда можете рассмотреть возможность использования собственной целевой очереди.

  • Ради будущих читателей, этот шаблон, очевидно, применяется, когда приложение действительно работает. Если приложение приостановлено, таймеры не срабатывают. Если вы хотите, чтобы пользователь был уведомлен в назначенное время, даже если приложение в данный момент не запущено, вы должны использовать локально запланированный уведомление пользователя.

Спасибо за подробный ответ @Rob. Это именно то, что я искал.

Shackleford 20.03.2022 22:03

Другие вопросы по теме