我已经建造了一个&#34;金属探测器&#34;一种运行&#34; calculateDistance&#34;的应用程序每2秒计算一次,用于计算用户位置和设定标记之间的距离,将其设置为var globalDistance。为了简单起见,取决于该距离是> = 10米还是<10米,我正在播放一个预定的计时器,该计时器调用&#34; audioplayer&#34;功能,发出哔哔声&#34;每2秒发声(如果距离> = 10m)或每0.5秒发声一次(如果距离<10m)。
问题是,定时器在我指示时永远不会失效。因此,如果我使用我的设备从<10m移动到> 10m,则0.5秒的蜂鸣声继续。我使用audioTimer.invalidate()来停止从上一次迭代运行的计时器。
知道我的代码出错了吗?非常感谢
func calculateDistance {
//here there is code that successfully calculates distance, every 2 seconds
var timerSeconds = 0.0
var audioTimer = Timer.scheduledTimer(timeInterval: (timerSeconds), target: self, selector: #selector(googleMaps.audioPlayer), userInfo: nil, repeats: true)
if globalDistance > 10 { // globalDistance is where i set the distance every 2 seconds, with a timer fired on ViewDidLoad
timerSeconds = 2
}
if globalDistance >= 0 && globalDistance <= 10 {
timerSeconds = 0.5
}
audioTimer.invalidate()
audioTimer = Timer.scheduledTimer(timeInterval: (timerSeconds), target: self, selector: #selector(googleMaps.audioPlayer), userInfo: nil, repeats: true)
audioTimer.fire()
}
func audioPlayer(){
AudioServicesPlaySystemSound(1104)
}
答案 0 :(得分:2)
基本思想是确保在没有停止任何先前的Timer
的情况下没有代码路径。您当前的代码有几条路径,在开始下一个定时器之前,现有的定时器不会失效。
此外,如果新的蜂鸣声频率与旧的蜂鸣声频率不同,我建议您仅使旧的Timer
无效。 (为什么无效2秒重复蜂鸣声并启动另一个计时器,如果旧的2秒计时器可以正常工作?)
所以,这意味着你会:
Timer
和TimeInterval
变量拉出函数; 例如:
private var audioTimer: Timer?
private var beepInterval: TimeInterval?
private func updateBeepIntervalIfNeeded() {
// here there is code that successfully calculates distance, called with whatever frequency you want
let newBeepInterval: TimeInterval
if globalDistance > 10 {
newBeepInterval = 2
} else if globalDistance >= 0 {
newBeepInterval = 0.5
} else {
fatalError("less than 0?!") // I'm inferring from your code that this cannot happen, but by using `let` above, Swift warned me that we had a path of execution we hadn't previously considered
}
if beepInterval != newBeepInterval {
beepInterval = newBeepInterval
audioTimer?.invalidate()
audioTimer = Timer.scheduledTimer(timeInterval: beepInterval!, target: self, selector: #selector(beep(_:)), userInfo: nil, repeats: true)
audioTimer!.fire()
}
}
@objc func beep(_ timer: Timer) {
// perform beep here
}
答案 1 :(得分:1)
这里有几个问题。
首先,我想强调引用和实例之间的区别。当您调用call初始化程序时,系统会为新对象分配一块内存,并为该内存提供引用,该内存存储在您为其分配的任何变量中。您可以将此引用分配给其他变量,这些变量将创建引用的副本。这些变量中的每一个都引用相同的原始对象。该对象将继续存在于内存中,直到没有更多变量引用它为止。
在您的情况下,您不是直接调用初始化程序,而是调用静态方法,该方法具有类似的用途。代表您分配一个新对象,然后为您提供一个引用,然后将其分配给audioTimer
。然而,有一个问题。当您调用Timer.scheduledTimer(timeInterval:target:selector:userInfo:repeats:)
时,新构造的计时器将在当前运行循环中为您安排。运行循环是在正确的时间启动计时器的负责人。这样做的结果是,现在runloop正在引用你的计时器,防止计时器对象被破坏。除非您使计时器无效以从其runloop取消注册,否则计时器将继续存在并永远触发,即使您删除了对它的依赖。
现在让我们看看你的代码,并解释一下发生了什么:
func calculateDistance {
//here there is code that successfully calculates distance, every 2 seconds
var timerSeconds = 0.0
// 1) Initialize timer #1
var audioTimer = Timer.scheduledTimer(timeInterval: (timerSeconds), target: self, selector: #selector(googleMaps.audioPlayer), userInfo: nil, repeats: true)
if globalDistance > 10 { // globalDistance is where i set the distance every 2 seconds, with a timer fired on ViewDidLoad
timerSeconds = 2
}
if globalDistance >= 0 && globalDistance <= 10 {
timerSeconds = 0.5
}
// 2) Invalidate timer #1 (timer #1 is useless)
audioTimer.invalidate()
// 3) Initialize timer #1
audioTimer = Timer.scheduledTimer(timeInterval: (timerSeconds), target: self, selector: #selector(googleMaps.audioPlayer), userInfo: nil, repeats: true)
// 4) Fire timer #2 immediately
audioTimer.fire()
} // At the end of this method body:
// - Timer #2 was never invalidated
// - audioTimer no longer references Timer #2, but:
// - Timer #2's runloop still references it, keeping it alive
// - Timer #2 is leaked
// ... and will continue firing forever.
func audioPlayer(){
AudioServicesPlaySystemSound(1104)
}
我们可以看到Timer
在第一部分中生成,它应该在timerSeconds
秒内触发0.在第2部分,该计时器无效。即使Timer
要在0秒内触发,几乎可以肯定它的运行循环还没有机会发射它。因此,创建此时间,从不触发,然后失效。它根本没有理由存在。
然后,在第3节中,创建并安排了定时器#2。它在第4部分手动触发,然后永久泄漏。
您需要一个保存对计时器的引用的实例变量。如果没有这个,你就无法使已经安排的计时器失效。
其次,您需要在适当的时间使计时器无效。
我建议你看一下Rob的答案。
答案 2 :(得分:0)
你创建一个新的,无限重复的计时器,立即使它失效(为什么?),然后创建另一个(为什么?),这是永远泄露的。
答案 3 :(得分:0)
您正在创建一个新计时器,无效,然后再次创建计时器。
您可以尝试创建计时器,并在调用audioPlayer函数时,根据timerSeconds变量的值检查要播放的声音。