为啥在块外设置时,`scheduledTimer` 会正确触发,而不是在块内?

Posted

技术标签:

【中文标题】为啥在块外设置时,`scheduledTimer` 会正确触发,而不是在块内?【英文标题】:Why would a `scheduledTimer` fire properly when setup outside a block, but not within a block?为什么在块外设置时,`scheduledTimer` 会正确触发,而不是在块内? 【发布时间】:2016-07-02 20:15:40 【问题描述】:

以下代码 sn-p 在完成块外调用时可以正常工作,但是当我在块内设置计时器时,它永远不会触发。我不明白为什么会有区别:

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

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

block 是一个完成处理程序,在请求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 早期版本的语法,请参阅previous revision of this answer。

【讨论】:

感谢很有意义。我应该想到的。谢谢。 有没有理由不使用DispatchSourceTimer而只使用计时器? 如果在主队列上运行,我经常倾向于Timer,因为它很简单。但是,如果在后台队列上运行计时器,则调度计时器是正确的工具。【参考方案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

【讨论】:

以上是关于为啥在块外设置时,`scheduledTimer` 会正确触发,而不是在块内?的主要内容,如果未能解决你的问题,请参考以下文章

为啥以及何时应该在块的末尾使用逗号?

为啥实例变量在块内时似乎消失了?

【iOS13】为啥调用方法`retrieveConnectedPeripheralsWithServices:`不返回任何外设

为啥 CoreBluetooth 一次又一次地发现相同的外设?

为啥配置exti外设时,开启的是afio的时钟?

Timer.scheduledTimer 在 Swift 3 中不起作用