为什么“scheduledTimer”在块外设置时会正确触发,但在块内却不能?

Why would a `scheduledTimer` fire properly when setup outside a block, but not within a block?

以下代码片段在完成块外调用时运行完美,但当我在块内设置时,计时器永远不会触发。我不明白为什么会有差异:

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

最初在块外调用它时我没有使用自引用,但一旦进入块内,就需要它。然而,我再次在块外测试了完全相同的代码,它仍然有效。

该块是一个完成处理程序,在请求 HealthKit 相关信息的许可后调用。

问题是有问题的完成块可能不在主线程上 运行ning,因此没有 运行 循环。但是需要在 运行 循环中安排定时器,虽然主线程有一个,但大多数后台线程没有(除非你自己添加一个)。

要解决此问题,请在该完成处理程序中将计时器的创建分派回主线程,它应该可以正常工作:

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 早期版本的语法,请参阅 previous revision of this answer

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)

这听起来很明显,但我遇到了类似的问题,计时器就是不会触发,原因是它不在主线程中...没有错误,只是从未触发。

放入主线程,至少你有机会尝试它!

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