首页 > 解决方案 > 杀死并重新运行后台计时器(DispatchSourceTimer)

问题描述

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

在我需要显示计时器的游戏屏幕中,我有一个显示经过时间的标签和 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)
        }
    }

标签: swiftgrand-central-dispatchnstimerdispatch-async

解决方案


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

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

        ...
    }
}

这修复了强参考周期。

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


推荐阅读