Swift 终止并重新运行后台计时器(DispatchSourceTimer)
在游戏屏幕上,我使用一个后台队列来计算游戏中经过的时间,这个函数基于一个完美的解决方案:它允许用户在计时器仍然打开的情况下浏览其他VC。 VC层次结构非常简单:游戏设置VC在Tabbar控制器中处理。游戏屏幕是分开的。用户可以在计时器打开时更改设置。设置存储在CoreData中 在我需要显示计时器的游戏屏幕中,我有一个标签,显示经过的时间和两个按钮:播放/暂停按钮和重置按钮 我在ViewDidLoad中调用我的设置计时器func。 my timer的默认值是CoreData中存储的值,它已在设置中定义。当定时器打开时,该值每秒递增1。 我还有一个静态let共享,它保持计时器状态(恢复/挂起) 当我在游戏屏幕上时,如果我的计时器被暂停,我的“播放/暂停”按钮工作正常:我可以导航到其他视图(意味着退出我的屏幕游戏),再次显示我的屏幕游戏并恢复计数器。它正确地更新了我的标签 问题是当我在计时器运行时关闭游戏屏幕视图时。计时器工作(print func显示计时器仍在运行),但当我再次显示屏幕时,我无法暂停/恢复/重新启动它,并且我的标签在我回来的那一刻就停止了。。。当计时器仍在运行时Swift 终止并重新运行后台计时器(DispatchSourceTimer),swift,grand-central-dispatch,nstimer,dispatch-async,Swift,Grand Central Dispatch,Nstimer,Dispatch Async,在游戏屏幕上,我使用一个后台队列来计算游戏中经过的时间,这个函数基于一个完美的解决方案:它允许用户在计时器仍然打开的情况下浏览其他VC。 VC层次结构非常简单:游戏设置VC在Tabbar控制器中处理。游戏屏幕是分开的。用户可以在计时器打开时更改设置。设置存储在CoreData中 在我需要显示计时器的游戏屏幕中,我有一个标签,显示经过的时间和两个按钮:播放/暂停按钮和重置按钮 我在ViewDidLoad中调用我的设置计时器func。 my timer的默认值是CoreData中存储的值,它已在设置
private var counter: Int16?
var t = RepeatingTimer(timeInterval: 1)
let gameIsOn = isGameOnManager.shared
override func viewDidLoad() {
super.viewDidLoad()
print("is timer On ? \(String(describing: gameIsOn.isgameOn))")
buildTimer()
if gameIsOn.isgameOn == true {
resumeTapped = true
t.resume()
PlayB.setImage(UIImage(named:"pause"), for: .normal)
} else {
resumeTapped = false
}
}
func buildTimer(){
self.t.eventHandler = {
self.counter! += 1
print("counter \(String(describing: self.counter!))")
self.coreDataEntity?.TimeAttribute = self.counter ?? 0
self.save()
DispatchQueue.main.async {
self.dataField.text = String(describing: self.counter ?? 0)
}
}
}
@objc func didTapButton(_ button: UIButton) {
if resumeTapped == false {
t.resume()
resumeTapped = true
gameIsOn.isgameOn = true
PlayB.setImage(UIImage(named:"pause"), for: .normal)
}
else if resumeTapped == true {
t.suspend()
resumeTapped = false
gameIsOn.isgameOn = false
PlayB.setImage(UIImage(named:"play"), for: .normal)
}
}
在视图控制器、计时器和计时器的事件处理程序之间有一个强引用循环。您应该在闭包中使用
弱
引用来打破此循环:
func buildTimer() {
t.eventHandler = { [weak self] in
guard let self = self else { return }
...
}
}
这修正了强引用循环
但当您修复此问题时,当您关闭此视图控制器时,可能会看到计时器停止运行。如果是这种情况,则表明计时器不在正确的对象中。它应该存在于整个应用程序中的某个更高级别的对象中,而不是在显示和取消的视图控制器中。什么是“离开”和“回来”?在许多情况下,视图控制器在离开时会被拆下。当你离开时,你需要将当前时间保存到永久性存储中,这样当你返回时,你就可以从现在开始自动启动计时器。@matt,当“我回来”(我指的是改变视图并回到我的VC)时,它已经将计数器值存储在CoreData中,因此该值是可以的,但不再实现,它不会重新启动。。。我没有写完整的函数,但是我的save函数还可以。如果你需要帮助,请提供足够的代码来重现这个问题。你有很多代码,只是在你的问题中提到的。我们无法想象或猜测它的作用。@matt,我编辑了存储计数器值的函数。@Rob,我重新编辑了我的问题,似乎我的问题更多的是关于我停止(杀死)并重新启动后台计时器的方式,而不是视图层次结构的问题,但我同意,这并不清楚……你让我开心!DispatchSourceTimer确实具有强引用。因此,您的解决方案完美地保持了我的按钮连接和标签更新。当我关闭游戏屏幕时,计时器功能齐全。。。令人惊讶的是,当我重新呈现VC:counter标签时,我的打印功能中有两个计数器。。。它重新创建了一个新的计时器。。。因此,为了确保只保留1个dispatchSourceTimer,我将我的计时器分配为共享let,并且每次呈现它时,我清空timer.eventHandler,取消计时器并重建一个。这有点棘手,但它是有效的。。。