Почему плагин `schedTimer` срабатывает должным образом при настройке вне блока, но не внутри блока?

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

self.timer = Timer.scheduledTimer(timeInterval: 1,
                                  target: self,
                                  selector: #selector(self.foo),
                                  userInfo: nil,
                                  repeats: true)

Я не использовал собственные ссылки при вызове изначально вне блока, но затем один раз внутри, это требовалось. Однако я снова проверил один и тот же код за пределами блока, и он все еще работает.

Этот блок является обработчиком завершения, который вызывается после запроса разрешения для связанной информации HealthKit.

Ответ 1

Проблема в том, что рассматриваемый блок завершения, вероятно, не выполнялся в главном потоке и поэтому не имел цикла выполнения. Но таймеры должны быть запланированы в цикле выполнения, и, хотя у основного потока он есть, у большинства фоновых потоков нет (если вы сами не добавите один).

Чтобы исправить это, в этом обработчике завершения отправьте создание таймера обратно в основной поток, и он должен работать нормально:

DispatchQueue.main.async {
    self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
}

Или используйте таймер источника отправки (таймер, который может быть запланирован для фоновой очереди и не требует цикла выполнения).

var timer: DispatchSourceTimer!

private func startTimer() {
    let queue = DispatchQueue(label: "com.domain.app.timer")
    timer = DispatchSource.makeTimerSource(queue: queue)
    timer.setEventHandler { [weak self] in
        // do something
    }
    timer.schedule(deadline: .now(), repeating: 1.0)
    timer.resume()
}

Синтаксис более ранней версии Swift приведен в предыдущей редакции этого ответа.

Ответ 2

Еще одна причина, почему Timer() может не работать - это то, как он создан. У меня была та же проблема, и все, что я пробовал, не решало ее, включая создание экземпляров в главном потоке. Я долго смотрел на это, пока не понял (тупо), что создаю это по-другому. Вместо Timer.scheduledTimer

Я создал его с помощью

let timer = Timer(timeInterval: 4.0, target: self, selector: #selector(self.timerCompletion), userInfo: nil, repeats: true)

В моем случае мне пришлось добавить его в цикл выполнения, чтобы запустить его. Как это

RunLoop.main.add(timer, forMode: RunLoop.Mode.default)

Ответ 3

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

Положите в основной поток, и, по крайней мере, у вас есть шанс на это!

 DispatchQueue.main.async {
  //insert your timer here
}