我创建一个计时器,每5秒钟调用一次它的块。然后我让应用程序进入背景,过一会儿进入前景。但是有时它可以快速调用该块。
let _ = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { (timer) in
print("--------")
}
当我进入前景时,第一次打印和第二次打印的间隔有时会小于一秒。在这种情况下时间间隔无效吗?
答案 0 :(得分:2)
要了解此行为,您需要了解NSTimer
和RunLoop
的工作方式。简单来说,RunLoop
将检查Timer是否应触发,如果是,它将通知Timer触发选择器,否则将不触发。现在,由于您处于后台,因此RunLoop
不会检查事件,因此无法通知计时器。但是一旦到达前台,即使通过了fireDate,它也会需要通知Timer。
时间线图:
让A(第5秒)和B(第10秒)成为计时器触发事件。安排在计时器Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true)
C进入背景(0秒)
D回到前台(第9秒,在A和B之间)。
-----> A ------> B
C ---------> D
说明:
在C上,运行循环将被暂停。因此,直到RunLoop恢复对事件D的处理后,才可以处理事件A。在事件D上,它将看到事件A应该触发,以便通知计时器。一秒钟后,RunLoop将看到事件B已经发生,因此它将再次通知计时器。这种情况说明了为什么您的事件每隔一秒就会打印一次。只是延迟的事件处理使它似乎早于触发,而实际上却被延迟处理了。
Apple Doc:
计时器不是实时机制。如果发生计时器的触发时间 在长时间运行的循环标注中或运行循环处于以下模式时 没有监控计时器,直到下一次计时器才会启动 运行循环检查计时器。因此,实际时间 定时器触发可能要晚得多。
资源:What is an NSTimer's behavior when the app is backgrounded?,NSRunLoop和Timer Docs
建议:
一旦应用程序进入后台,请停止计时器,但要存储fireDate
。回到前台后,检查fireDate
是否超过Date()
。然后创建一个新的Timer来处理前台事件。
答案 1 :(得分:1)
当应用程序进入后台时,该应用程序将很快挂起,程序停止运行。当应用切换回前台时,将触发缓冲/延迟计时器事件,然后您会很快看到许多打印内容。