Das Problem
Hier gibt es auf der Hand einige Probleme gibt.
Zum einen möchte ich Betonung wie der Unterschied zwischen Referenzen und Instanzen. Wenn Sie einen Initialisierer aufrufen, ordnet das System einen Speicher für ein neues Objekt zu und gibt Ihnen eine Referenz an diesen Speicher, die in der Variablen gespeichert wird, der Sie ihn zuweisen. Sie können diese Referenz anderen Variablen zuweisen, die Kopien der Referenz erstellen. Jede dieser Variablen verweist auf dasselbe Originalobjekt. Dieses Objekt wird weiterhin im Speicher existieren, bis keine Variablen mehr darauf verweisen.
In Ihrem Fall rufen Sie nicht direkt einen Initialisierer auf, sondern Sie rufen eine statische Methode auf, die einem ähnlichen Zweck dient. Ein neues Objekt wird in Ihrem Auftrag zugewiesen, und Sie erhalten eine Referenz, die Sie dann audioTimer
zuweisen. Es gibt jedoch einen Haken. Wenn Sie Timer.scheduledTimer(timeInterval:target:selector:userInfo:repeats:)
aufrufen, wird der neu erstellte Zeitgeber für Sie in der aktuellen Ausführungsschleife geplant. Die Run-Schleife sorgt dafür, dass der Timer zum richtigen Zeitpunkt ausgelöst wird. Die Konsequenz daraus ist, dass jetzt der Runloop auf Ihren Timer verweist und verhindert, dass das Timer-Objekt zerstört wird. Sofern Sie Ihren Timer nicht für ungültig erklären, um ihn aus seiner Runloop-Liste zu entfernen, wird der Timer weiterhin bestehen bleiben und für immer feuern, selbst wenn Sie Ihre Einstellung dafür löschen.
Lassen Sie uns jetzt auf den Code werfen Sie einen Blick, mit einer Erklärung darüber, was los ist:
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)
}
Wir können sehen, dass ein Timer
ein in Abschnitt gemacht wird, was in timerSeconds
Sekunden abfeuern sollte, 0 In Abschnitt 2 wird dieser Timer ungültig gemacht. Auch wenn der Timer
in 0 Sekunden ausgelöst wurde, ist es fast sicher, dass seine Laufschleife noch keine Chance hatte, ihn auszulösen. Daher wird diese Zeit erstellt, nie ausgelöst und dann ungültig gemacht. Es gibt keinen Grund dafür, dass es dort überhaupt existiert.
Dann wird in Abschnitt 3 Timer # 2 erstellt und geplant. es wird manuell in Abschnitt 4 abgefeuert, und dann ist es permanent undicht.
Die Lösung
Sie benötigen eine Instanz-Variable, die Bezug auf die Timer hält. Ohne dies haben Sie keine Möglichkeit, den Timer, der bereits geplant wurde, ungültig zu machen.
Zweitens müssen Sie den Timer zum richtigen Zeitpunkt ungültig machen.
Ich schlage vor, Sie betrachten Robs Antwort für ein Beispiel.
Sie können einen Schalter verwenden, um Bereiche zu überprüfen :) – Alexander
Ja, ich Ich nehme das in Betracht, aber mit zwei offenen Bereichen ('> 10' und' <0') fügst du dann mindestens eine 'where'-Klausel hinzu, an der ich denke, dass eine einfache' if'-'else' ist viel intuitiver, IMHO. Es ist natürlich eine Frage der persönlichen Meinung ... – Rob
Wahr. Ich habe einen Präfix/Postfix Bereich Operator für halb offene Bereiche definiert, p – Alexander