在 Swift 上设置计时器

2024-04-09

我尝试重复执行函数 pepe() ,我没有收到错误,但它不起作用。

这是我的代码:

public class MyClass {
    var timer = Timer()
    @objc func pepe() -> String {
        let hola = "hola"
        return hola
    }
    func startTimer(){
         let seconds = 1.0
        timer = Timer.scheduledTimer(timeInterval: seconds, target: ().self, selector: #selector(pepe), userInfo: nil, repeats: false)

    }
    func stopTimer() {

        timer.invalidate()

    }

    init() {
        self.startTimer()
        self.stopTimer()
    }
}
var pepe = MyClass()
pepe.stopTimer()
pepe.startTimer()

我会建议:

  1. 不要实例化空的Timer。考虑:

    var timer = Timer()
    

    那就是创建一个空白的计时器实例。我们不想这样做。你应该使用:

    weak var timer: Timer?
    

    这实现了一些目标:

    • The Timer?语法表示它是一个“可选”,您稍后将实例化其值。

    • 当。。。的时候Timer已调度,runloop 对其保持强引用。因此,与大多数其他对象不同,您个人不需要保留对计划计时器的强引用。你可能想要timer变量自动设置为nil当定时器失效时。所以weak限定符表示当计时器无效时,timer变量将自动设置为nil.

  2. The pepe方法签名不太正确:

    • 它不应该返回任何东西;

    • 你可能应该给它Timer范围。这是一个值得养成的好习惯。您可能在这里不需要它,但它使该方法的意图更加清晰,您最终可能会发现它很有用Timer范围;和

    • 我可能会给它一个更具描述性的名称以避免任何歧义;我倾向于使用类似的名字timerHandler.

  3. 启动和停止计时器没有意义init.

  4. 该参考().self in the target应该只是self.

  5. 在你的操场上,你正在停止计时器(尚未安排启动)然后启动它。

    您可能还想在一段时间后停止它,这样您就有机会看到Timer在行动中。

  6. 但是,作为一般规则,在编写启动计时器的方法时,请谨慎确保您没有(意外地)启动它。如果您不这样做,并且不小心调用了startTimer两次,最终可能会导致多个计时器同时运行(最糟糕的是,丢失了对较早计时器的引用)。一种常见的解决方案典型的解决方案是查看是否已有计时器,如果有,则在创建下一个计时器之前将其无效。这可以通过可选的链接模式轻松完成:

    func startTimer() {
        timer?.invalidate()   // stops previous timer, if any
    
        // now proceed with scheduling of new timer
    }
    

Thus:

import UIKit

// if doing this in playground, include the following two lines

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

// MyClass

public class MyClass {
    weak var timer: Timer?

    @objc func timerHandler(_ timer: Timer) {
        let hola = "hola"
        print(">>>> \(hola)")
    }

    func startTimer() {
        timer?.invalidate()   // stops previous timer, if any

        let seconds = 1.0
        timer = Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(timerHandler(_:)), userInfo: nil, repeats: true)
    }

    func stopTimer() {
        timer?.invalidate()
    }
}

var object = MyClass()
object.startTimer()

// stop it 10 seconds later
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
    object.stopTimer()
}

不过,应该认识到,您最终可能会得到类似于强引用循环的东西:

  • runloop 保留对预定计时器的强引用;
  • 基于选择器的计时器对其保持强引用target;
  • The MyClass (the target) 可能是导致该计时器最终失效的原因。

因此,MyClass不能被释放,直到Timer无效。而且,就目前情况而言,你不能只是invalidate the Timer in the deinit of MyClass, 因为deinit在计时器失效之前不会被调用。

最终效果是,如果你有,例如,这个MyClass作为视图控制器的属性并启动计时器,然后关闭视图控制器,计时器将继续运行并且MyClass不会被释放。

为了解决这个问题,您可能需要使用基于闭包的计时器[weak self]参考,消除了定时器和之间的强参考MyClass。然后,您还可以在以下情况下自动使计时器无效:MyClass被释放:

public class MyClass {
    weak var timer: Timer?

    deinit {
        timer?.invalidate()
    }

    func timerHandler(_ timer: Timer) {
        let hola = "hola"
        print(">>>> \(hola)")
    }

    func startTimer() {
        timer?.invalidate()   // stops previous timer, if any

        let seconds = 1.0
        timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: true) { [weak self] timer in
            self?.timerHandler(timer)
        }
    }

    func stopTimer() {
        timer?.invalidate()
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Swift 上设置计时器 的相关文章

随机推荐