终止并重新运行后台计时器(调度源计时器)



在游戏屏幕中,我使用后台队列来计算游戏中经过的时间,这个功能基于Daniel Galasko解决方案,这是我的应用程序的完美解决方案:它允许用户在计时器仍处于打开状态时浏览其他VC。 VC的层次结构非常简单:游戏设置VC在tabBarController中处理。游戏屏幕是分开的。用户可以在计时器打开时更改设置。设置存储在核心数据中。

在我的游戏屏幕中,我需要显示计时器,我有一个显示经过时间和 2 个按钮的标签:播放/暂停按钮和重置按钮。

我在ViewDidLoad中调用我的设置计时器函数。 我的计时器的默认值是存储在 CoreData 中的值,它已在设置中定义。当计时器打开时,此值每秒递增 1。 我还有一个静态共享,保持计时器状态(恢复/暂停(。

当我在游戏屏幕上时,如果我的计时器被挂起,我的播放/暂停按钮可以完美地工作:我可以导航到其他视图(意味着关闭我的屏幕游戏(,再次呈现我的屏幕游戏并恢复我的计数器。它会正确更新我的标签。

问题是当我在计时器运行时关闭游戏屏幕视图时。计时器工作(打印功能显示计时器仍在运行(,但是当我再次显示屏幕时,我无法暂停/恢复/重新启动它,并且我的标签在我回来的那一刻停止了......而计时器仍在运行。

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)
}
}

视图控制器、计时器和计时器的事件处理程序之间有一个强大的引用周期。您应该在闭包中使用weak引用来打破此循环:

func buildTimer() {
t.eventHandler = { [weak self] in
guard let self = self else { return }
...
}
}

这修复了强参考周期。

但是,当您解决此问题时,当您关闭此视图控制器时,您可能会看到计时器停止运行。如果是这种情况,则表明计时器不在正确的对象中。它应该位于整个应用程序中保留的某个更高级别的对象中,而不是在此呈现和关闭的视图控制器中。