循环计时器不会按指示无效

时间:2016-11-08 19:42:41

标签: swift timer

我已经建造了一个&#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)
}

4 个答案:

答案 0 :(得分:2)

基本思想是确保在没有停止任何先前的Timer的情况下没有代码路径。您当前的代码有几条路径,在开始下一个定时器之前,现有的定时器不会失效。

此外,如果新的蜂鸣声频率与旧的蜂鸣声频率不同,我建议您仅使旧的Timer无效。 (为什么无效2秒重复蜂鸣声并启动另一个计时器,如果旧的2秒计时器可以正常工作?)

所以,这意味着你会:

  • TimerTimeInterval变量拉出函数;
  • 只有在确定蜂鸣声间隔发生变化时才执行“新定时器”过程;和
  • 确保在创建新计时器之前始终使旧计时器无效。

例如:

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变量的值检查要播放的声音。

相关问题