我建议:
不要实例化一个空的 Timer
。考虑:
var timer = Timer()
这将创建一个空的计时器实例,但我们不想这样做。相反,您应该使用:
weak var timer: Timer?
这样做有几个好处:
pepe
方法签名不太正确:
在 init
中启动和停止计时器没有意义。
在 target
中引用 ().self
应该改为 self
。
在您的 playground 中,您正在停止尚未启动的计时器然后再启动它。
您可能还希望在一段时间后停止它,这样您就有机会看到 Timer
的效果。
但是,作为一般规则,在编写启动计时器的方法时,最好确保您没有(意外地)已经启动了它。如果您不这样做,并意外调用了两次 startTimer
,您可能会同时启动多个计时器(更糟糕的是,丢失对早期计时器的引用)。一个常见的解决方案是在创建下一个计时器之前查看是否已经有计时器存在,如果有,则使其失效。这可以使用可选链模式轻松实现:
func startTimer() {
timer?.invalidate()
}
因此:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
public class MyClass {
weak var timer: Timer?
@objc func timerHandler(_ timer: Timer) {
let hola = "hola"
print(">>>> \(hola)")
}
func startTimer() {
timer?.invalidate()
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()
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
object.stopTimer()
}
然而,需要认识到,你可能会得到类似于强引用循环的东西:
- 运行循环对预定计时器保持强引用;
- 基于选择器的计时器对其
目标
保持强引用;
MyClass
(即target
)可能负责最终使该计时器无效。
因此,只有在Timer
被使无效之前,MyClass
才能被解除分配。并且,按照现状,在MyClass
的deinit
中无法仅仅invalidate
Timer
,因为直到定时器失效之前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()
let seconds = 1.0
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: true) { [weak self] timer in
self?.timerHandler(timer)
}
}
func stopTimer() {
timer?.invalidate()
}
}