Swift - 计时器不停止

6

I have the following code:

class firstVC: UIViewController {
    var timer : Timer?

    func scheduledTimerWithTimeInterval(){
        timer = Timer.scheduledTimer(timeInterval: 60, target: self, 
        selector: #selector(self.anotherFunc), userInfo: nil, repeats: 
        true)
    }

    override func viewDidAppear(_ animated: Bool) {
        scheduledTimerWithTimeInterval()
    }
}

我试图停止计时器,但没有成功:

func stopTimer() {
    if timer != nil {
        timer?.invalidate()
        timer = nil
    }
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    stopTimer()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    stopTimer()
}

我甚至尝试将停止函数放在applicationWillResignActiveapplicationDidEnterBackground中,但它没有停止:

firstVC().stopTimer()

非常感谢您的帮助,十分感激。

涉及到IT技术相关内容,请您指导。

2
你尝试过调试并查看 timer?.invalidate 是否被触发了吗? - CodeNinja
1
谁调用了scheduledTimerWithTimeInterval()函数?如果它被调用两次,你将创建2个计时器并且失去了第一个的轨迹。也许在创建计时器之前,你应该检查是否为nil - vacawama
viewDidAppear 通常会被调用两次。使用断点进行检查。 - arvidurs
不,但我知道计时器仍在运行。嗯,你真的应该测试一下。这可以消除可能出现的问题类别之一。你确实需要检查是否到达了你的“invalidate”行。 - Rob
如果你采用DuncanC的解决方案,它可以解决问题的一个来源(重复的Timer实例)。并且,你也说过有两个好的答案,但是你的计时器仍然在运行。那么,也许你可以澄清一下具体是如何做到的(按home键?关闭此视图?等等)。此外,你有开启某些后台模式吗?总之,下面的答案解决了明显的问题,但如果问题仍然存在,我们需要更多细节。我们需要一个可重现的问题示例。 - Rob
显示剩余3条评论
5个回答

12

正如其他人所说,你在启动新计时器之前未清除旧计时器并且正在创建多个计时器。你需要确保在启动新计时器之前停止任何当前计时器。

我做的是使我的计时器变为弱引用(weak)。然后在尝试创建新计时器之前使用代码 `myTimer?.invalidate() 来停止旧计时器:

class firstVC: UIViewController {
    weak var timer : Timer?

    func scheduledTimerWithTimeInterval(){
        timer?.invalidate()
        timer = Timer.scheduledTimer(timeInterval: 60, target: self, 
        selector: #selector(self.anotherFunc), userInfo: nil, repeats: 
        true)
    }
}

通过使您的计时器弱引用,唯一保持计时器强引用的是运行循环。然后,当您使其无效时,它会立即被释放并设置为nil。通过使用可选链接调用invalidate方法,如果已经是nil,它不执行任何操作,并且如果它正在运行,则停止并使其变为nil。

请注意,此方法仅适用于使用scheduledTimer()工厂方法之一一次性创建计时器的情况。如果您尝试先创建计时器然后再将其添加到运行循环中,则必须使用一个强局部变量来创建它,否则它会在创建后立即释放。


1
完成这个步骤后,你的计时器将只有一个实例在运行。然后,你可以随时使用语句 timer?.invalidate() 安全地停止计时器。 - Duncan C
@Yossi,你找到解决方案了吗?如果有的话,请分享一下。谢谢。 - Chetan kasundra
@Chetankasundra 我已经完成了,很快就会发布。 - Shlomi

2
问题在于您的计时器被实例化了多次,因此原始计时器失去了引用。 添加一个变量didStartTimer = false。 然后在viewDidAppear中进行验证,然后调用计时器函数。 这样就可以解决问题了。
像这样:
class firstVC: UIViewController {
var timer : Timer?
var didStartTimer = false

func scheduledTimerWithTimeInterval(){
    timer = Timer.scheduledTimer(timeInterval: 60, target: self, 
    selector: #selector(self.anotherFunc), userInfo: nil, repeats: 
    true)
}

override func viewDidAppear(_ animated: Bool) {
            if !didStartTimer {
                 scheduledTimerWithTimeInterval()
                 didStartTime = true
            }

}

1
不需要 didStartTimer 变量。只需检查 timer 变量即可。 - rmaddy
我不太确定,因为viewDidAppear通常会被调用两次,所以我在验证它时只保留一个计时器实例运行。 - arvidurs
2
你可以简单地检查 timer 是否为 nil。不需要额外的变量。 - rmaddy

2

经过研究,我找到了一个解决方案,
对我有效的唯一方法是在AppDelegate文件中创建函数,并在需要时调用它们,
以下是代码,timerSwitch函数:

最初的回答:
通过研究,我发现了一个解决方案。对我有效的唯一方法是在AppDelegate文件中创建函数,并在需要时调用它们。以下是代码,timerSwitch函数:

    func timerSwitch()
    {
        if (timerStatus) {
            checkStateTimer = Timer.scheduledTimer(
                timeInterval: 60, 
                target:self, 
                selector: #selector(self.yourFunction), 
                userInfo: nil, repeats: true)
        } else {
            checkStateTimer?.invalidate()
        }
    }

    func stopTimer()
    {
        timerStatus = false
        timerSwitch()

    }

    func startTimer()
    {
        timerStatus = true
        timerSwitch()

    }

'yourFunction'是当定时器开始时要执行的函数,
在我的情况下是发送心跳包。
然后我在AppDelegate中调用了以下函数timerSwitch:


    func applicationWillResignActive(_ application: UIApplication) {
        stopTimer()
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        stopTimer() 
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        startTimer()
    }

1

什么都不起作用,最后使用self才解决了问题!!!

 self.atimer?.invalidate()
 self.atimer = Timer()

我没有给你点踩,但我怀疑你使用强制解包的原因可能是导致了代码崩溃。如果 atimer 是 nil,那么这段代码就会崩溃。最好使用可选链式形式 self.atimer?.invalidate()。如果计时器为 nil,该代码将安全地不执行任何操作,如果它不是 nil,则使其失效。 - Duncan C
谢谢Duncan,你说得对,那是我的错误,我会编辑答案。 - guru
我还建议使用Timer的便利方法scheduledTimer(timeInterval:target:selector:userInfo:repeats:) 或者scheduledTimer(withTimeInterval:repeats:block:)。如果你使用默认的初始化器Timer(),那么你就必须配置计时器并将其添加到运行循环中。scheduledTimer方法可以一步完成所有操作。 - Duncan C
它可以运行,哇塞,苹果的尴尬技巧。 - Yılmaz edis

0
作为调试步骤,请尝试将 var timer = Timer() 放在全局空间(例如,在文件顶部的 import 语句下方)以确保只创建并引用一个 Timer 对象。如果在函数内部声明 Timer,则每次调用该函数时都会创建一个新的计时器,这会导致您失去对旧计时器的引用,最终无法停止它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接