在Swift上设置Timer

Jua*_*eau 2 timer ios swift swift3

我试图重复执行函数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()
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 10

我会建议:

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

    var timer = Timer()
    
    Run Code Online (Sandbox Code Playgroud)

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

    weak var timer: Timer?
    
    Run Code Online (Sandbox Code Playgroud)

    这实现了一些事情:

    • Timer?语法说,这是一个"可选",其值你以后会实例.

    • Timer安排时,runloop会对其进行强有力的引用.因此,与大多数其他对象不同,您个人不需要对预定的计时器进行强有力的引用.并且您可能希望timer变量nil在计时器失效时自动设置为.因此,weak限定符表示当计时器失效时,timer变量将自动设置为nil.

  2. pepe方法的签名是不完全正确:

    • 它不应该归还任何东西;

    • 你应该给它Timer参数.进入是一个很好的习惯.您可能不需要它,但它使方法的意图更加清晰,您最终可能会发现使用该Timer参数很有用; 和

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

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

  4. 这参照().selftarget应该只是self.

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

    您可能还希望在一段时间后停止它,这样您就有机会看到实际Timer操作.

  6. 但是,作为一般规则,在编写启动计时器的方法时,谨慎的做法是确保您没有(意外地)已经启动它.如果你不这样做,并且不小心打了startTimer两次电话,最终可能会有多个定时器同时出现(最糟糕的是,丢失了对早期定时器的引用).一个常见的解决方案是典型的解决方案是查看是否已有计时器,如果是,则在创建下一个计时器之前使其无效.使用可选的链接模式很容易实现:

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

从而:

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()
}
Run Code Online (Sandbox Code Playgroud)

但应该认识到,您最终可能会得到类似于强参考周期的东西:

  • runloop对预定的计时器有很强的参考价值;
  • 基于选择器的计时器保持对它的强引用target;
  • MyClass(在target)什么想必是负责最终该定时器无效.

因此,MyClassTimer无效之前无法取消分配.而且,因为它代表,你不能随便invalidateTimerdeinitMyClass,因为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()
    }
}
Run Code Online (Sandbox Code Playgroud)


Sh_*_*han 3

设置repeats = true ,不要self这样().self,我试过了,它有效,而且你也犯了一个错误,试图启动和停止计时器init

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

    }
    func stopTimer() {

        timer.invalidate()

    }

    init() {

    }
}
Run Code Online (Sandbox Code Playgroud)