我尝试多次执行函数 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()
最佳答案
我建议:
不要实例化一个空的
Timer
。考虑:var timer = Timer()
那是创建一个空白的计时器实例。我们不想那样做。您应该改用:
weak var timer: Timer?
这实现了一些事情:
Timer?
语法表明它是一个“可选的”,您稍后将实例化它的值。当
Timer
被调度时,runloop 会保持对它的强引用。因此,与大多数其他对象不同,您个人不需要保留对计划计时器的强引用。当计时器失效时,您可能希望timer
变量自动设置为nil
。因此,weak
限定符表示当计时器失效时,timer
变量将自动设置为nil
。
pepe
方法签名不太正确:它不应该返回任何东西;
您应该给它
Timer
参数。养成好习惯。您在这里可能不需要它,但它使方法的意图更加清晰,您最终可能会发现使用Timer
参数很有用;和我可能会给它起一个更具描述性的名称以避免歧义;我倾向于使用像
timerHandler
这样的名称。
在
init
中启动和停止计时器没有意义。target
中对().self
的引用应该只是self
。在您的 playground 中,您正在停止计时器(尚未安排启动)然后启动它。
您可能还想在一段时间后停止它,这样您就有机会看到
Timer
的运行情况。但是,作为一般规则,在编写启动计时器的方法时,确保您没有(意外地)启动它是明智的。如果您不这样做,并且不小心调用了
startTimer
两次,您可能会导致多个计时器同时运行(最糟糕的是,丢失了对早期计时器的引用)。一种常见的解决方案典型的解决方案是查看是否已经存在计时器,如果存在,则在创建下一个计时器之前使其无效。这可以通过可选的链接模式轻松实现:func startTimer() { timer?.invalidate() // stops previous timer, if any // now proceed with scheduling of new timer }
因此:
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 保持对预定定时器的强引用;
- 基于选择器的计时器对其
目标
保持强引用; MyClass
(target
)大概负责最终使该计时器无效。
因此,在 Timer
失效之前,MyClass
无法被释放。而且,就目前而言,您不能只是在 MyClass
的 deinit
中 invalidate
Timer
,因为 deinit
在计时器失效之前不会被调用。
最终效果是,如果你有,例如,这个 MyClass
作为你的 View Controller 的属性,启动计时器然后关闭 View Controller ,计时器将继续运行并且 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()
}
}
关于ios - 在 Swift 上设置定时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48686960/