Technologische Neuigkeiten, Bewertungen und Tipps!

Arbeiten mit Timern in Swift

Hinweis: Der folgende Artikel hilft Ihnen weiter: Arbeiten mit Timern in Swift

Zurück zum Blog

Timer sind in Swift sehr praktisch, von der Erstellung wiederkehrender Aufgaben bis hin zur verzögerten Planung von Arbeiten. In diesem Tutorial zur App-Entwicklung wird erläutert, wie Sie einen Timer in Swift erstellen.

Wir besprechen, wie Sie die Timer-Klasse, früher bekannt als NSTimer, zum Planen von Timern verwenden. Wir beschäftigen uns mit sich wiederholenden und sich nicht wiederholenden Timern, der Verwendung von Laufschleifen, dem Verfolgen von Timern und wie Sie deren Energie- und Leistungseinfluss reduzieren können.

Das Erstellen eines Wiederholungstimers in Swift ist überraschend einfach. Hier ist wie:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fire), userInfo: nil, Repeats: true)

@objc func fire()
{
print(“FEUER!!!”)
}

Im obigen Code passieren einige Dinge:

  • Ein Timer wird mit der Klassenmethode Timer.scheduledTimer(…) erstellt. Sein Rückgabewert wird dem konstanten Timer zugewiesen. Diese Konstante enthält nun einen Verweis auf den Timer, der später nützlich sein wird.
  • Die Parameter von ScheduledTimer() sind das Timer-Intervall von 1 Sekunde, das einen Mechanismus verwendet, der als Target-Action bekannt ist, einige UserInfos, die auf Null gesetzt sind, und der Parameter Repeats, der auf True gesetzt ist.
  • Wir haben auch eine Funktion fire() codiert. Dies ist die Funktion, die aufgerufen wird, wenn der Timer ausgelöst wird, also etwa jede Sekunde. Indem Sie target auf self und selector auf #selector(fire) setzen, geben Sie an, dass bei jedem Auslösen des Timers die Funktion fire() von self aufgerufen werden muss.

Der obige Code sollte in einem Klassenkontext ausgeführt werden, beispielsweise in einer View-Controller-Klasse. Die Funktion fire() ist Teil der Klasse und self verweist auf die aktuelle Klasseninstanz.

Sie können einen Timer auch mithilfe eines Verschlusses erstellen. So was:

let timer = Timer.scheduledTimer(withTimeInterval: 1.0, Wiederholungen: true, Block: { timer in
print(“FEUER!!!”)
})

Im obigen Code nimmt der letzte Parameterblock einen Abschluss an. Der Abschluss hat einen Parameter, den Timer selbst.

Der Timer mit target-action und self funktioniert auf einem Xcode-Playground nicht, es sei denn, Sie erstellen eine Klasse oder Struktur. Hier ist der Code, mit dem Sie in Xcode einen Timer auf einem Spielplatz erstellen können:

PlaygroundSupport importieren

PlaygroundPage.current.needsIndefiniteExecution = true

let timer = Timer.scheduledTimer(withTimeInterval: 1.0, Wiederholungen: true, Block: { timer in
print(“FEUER!!!”)
PlaygroundPage.current.finishExecution()
})

Im obigen Code setzen wir „needesIndefiniteExecution“ auf „true“, damit der Playground nicht automatisch aufhört zu laufen, wenn der Playground fertig ist. Der Timer kann auf diese Weise ausgelöst werden, und an diesem Punkt rufen wir finishExecution() auf, um den Spielplatz anzuhalten.

Dank der Trailing-Closure-Syntax können wir diesen abschlussbasierten Timer noch prägnanter gestalten. So was:

let timer = Timer.scheduledTimer(withTimeInterval: 1.0, Wiederholungen: true) { timer in
print(“FEUER!!!”)
}

Sehen Sie, wie die letzte Argumentbezeichnung weggelassen wird? Das liegt daran, dass der letzte Parameter ein Abschluss ist. Dadurch wird der Code prägnanter. Sauber!

Das @objc-Attribut macht die Funktion fire() in Objective-C verfügbar. Die Timer-Klasse ist Teil der Objective-C-Laufzeit, deshalb benötigen wir das @objc-Attribut.

Im vorherigen Beispiel haben wir 5 Parameter verwendet, um einen Timer zu erstellen. Sie sind:

  • timeInterval: Das Intervall zwischen den Auslösungen des Timers in Sekunden. Der Typ ist „Double“.
  • Ziel: eine Klasseninstanz, auf der die Funktion für den Selektor aufgerufen werden soll, häufig self
  • Selektor: die Funktion, die aufgerufen wird, wenn der Timer ausgelöst wird, mit #selector(…)
  • userInfo: ein Wörterbuch mit Daten, die dem Selektor bereitgestellt werden, oder Null
  • Wiederholungen: ob dieser Timer wiederholt oder nicht wiederholt

Das Erstellen eines sich nicht wiederholenden Timers ist so einfach wie das Setzen des Wiederholungsparameters auf „false“. Der Timer wird nur einmal ausgelöst und danach sofort ungültig.

Wie stoppt man dann einen Wiederholungstimer? Es ist einfach. Hier ist wie:

timer.invalidate()

Sie würden die Timer-Variable irgendwo verfolgen, beispielsweise über eine gespeicherte Eigenschaft der Klasse, in der Sie den Timer verwenden. Sie können diese Eigenschaft dann verwenden, um den Timer zu verwalten.

Sie können einem Timer auch einige zusätzliche Informationen hinzufügen, indem Sie userInfo verwenden. Dieser Wert wird über seinen Timer-Parameter an die Funktion gesendet, die auslöst. Hier ist ein Beispiel:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fire(timer:)), userInfo: [“score”: 10]wiederholt: wahr)

@objc func fire(timer: Timer)
{
if let userInfo = timer.userInfo as? [String: Int],
let score = userInfo[“score”] {

print(„Du hast \(score) Punkte erzielt!“)
}
}

Im obigen Code haben wir das Wörterbuch hinzugefügt [“score”: 10] zum Timer. Wenn es ausgelöst wird, wird dieses Wörterbuch als timer.userInfo an die Funktion fire(timer:) übergeben. Innerhalb der fire(timer:)-Funktion prüfen wir, ob die userInfo-Eigenschaft den erwarteten Typ hat und erhalten den richtigen Wert.

Schauen wir uns als Nächstes ein praktisches Beispiel für die Verwendung von Timern an.

Stellen Sie sich vor, Sie erstellen ein Spiel. Der Benutzer hat 60 Sekunden Zeit, um ein Rätsel zu lösen und Punkte zu sammeln. Während der Countdown-Timer abläuft, behalten Sie den Überblick darüber, wie viele Sekunden noch übrig sind.

Zuerst erstellen wir zwei Eigenschaften an der Spitze unserer Spielklasse. Etwas wie:

var timer:Timer?
var timeLeft = 60

Der Timer startet nicht sofort. Bis zum Start ist die Timer-Eigenschaft gleich Null. Irgendwann hat das Spiel begonnen, also starten wir auch den Timer:

timer = Timer.scheduledTimer(timeInterval: 1.0, Ziel: self, Selektor: #selector(onTimerFires), userInfo: nil, Wiederholungen: true)
Der Timer ruft onTimerFires() jede Sekunde auf unbestimmte Zeit auf. Und hier ist diese Funktion:

@objc func onTimerFires()
{
timeLeft -= 1
timeLabel.text = „\(timeLeft) verbleibende Sekunden“

if timeLeft <= 0 { timer?.invalidate() timer = nil } } Jedes Mal, wenn der Timer ausgelöst wird, subtrahiert er 1 von timeLeft und aktualisiert die Bezeichnung „verbleibende Zeit“. Wenn der Timer Null erreicht, wird der Timer ungültig und auf Null gesetzt. Dadurch wird effektiv ein Countdown-Timer erstellt, der von 60 auf Null herunterzählt. Und zu einem späteren Zeitpunkt können Sie den Countdown natürlich zurücksetzen und von vorne beginnen.

Timer funktionieren in Verbindung mit Laufschleifen. Ausführungsschleifen sind ein grundlegender Bestandteil von Threads und Parallelität.

Am einfachsten kann man sich Laufschleifen wie ein Riesenrad vorstellen. Menschen können in einen Pkw einsteigen und sich am Steuer fortbewegen. Auf ähnliche Weise können Sie eine Aufgabe in einer Ausführungsschleife planen. Die Ausführungsschleife „schleift“ ständig und führt Aufgaben aus. Wenn keine Aufgaben auszuführen sind, wartet die Ausführungsschleife oder wird beendet.

Die Art und Weise, wie Laufschleifen und Timer zusammenarbeiten, besteht darin, dass die Laufschleife prüft, ob ein Timer ausgelöst werden soll. Ein Timer ist kein Echtzeitmechanismus, da das Auslösen des Timers mit der Ausführung einer anderen Aufgabe durch den Runloop zusammenfallen kann. Man kann das damit vergleichen, ins Riesenrad einsteigen zu wollen, wenn am unteren Eingang noch kein Auto steht. Das Ergebnis ist, dass ein Timer später als geplant ausgelöst werden kann.

Das ist nicht unbedingt eine schlechte Sache. Vergessen Sie nicht, dass Sie Code auf einem mobilen Gerät ausführen, für das Energie- und Leistungsbeschränkungen gelten! Das Gerät kann Laufschleifen effizienter planen, um Strom zu sparen.

Sie können dem System auch helfen, Strom zu sparen, indem Sie eine Timereigenschaft namens Toleranz verwenden. Dadurch wird dem Planungssystem mitgeteilt: „Sehen Sie, ich möchte, dass dies jede Sekunde ausgeführt wird, aber es ist mir egal, ob es 0,2 Sekunden zu spät ist.“ Dies hängt natürlich von Ihrer App ab. Apple empfiehlt, die Toleranz für einen Wiederholungstimer auf mindestens 10 % der Intervallzeit einzustellen.

Hier ist ein Beispiel:

let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fire), userInfo: nil, Repeats: true)
timer.tolerance = 0,2

Die Toleranzeigenschaft verwendet dieselben Einheiten wie die timeInterval-Eigenschaft, daher verwendet der obige Code eine Toleranz von 200 Millisekunden.

Die Toleranz führt niemals dazu, dass ein Timer zu früh, sondern erst später ausgelöst wird. Und die Toleranz führt auch nicht dazu, dass ein Timer „driftet“, dh wenn ein Timer-Auslöser zu spät ist, hat dies keinen Einfluss auf die geplante Zeit des nächsten Timer-Auslösers.

Bei Verwendung der Klassenmethode Timer.scheduledTimer(…) wird der Timer im Standardmodus automatisch für die aktuelle Ausführungsschleife geplant. Dies ist normalerweise die Ausführungsschleife des Hauptthreads. Daher werden Timer möglicherweise nicht ausgelöst, wenn die Ausführungsschleife im Hauptthread ausgelastet ist, beispielsweise wenn der Benutzer Ihrer App mit der Benutzeroberfläche interagiert.

Sie können dieses Problem lösen, indem Sie den Timer selbst manuell in einer Laufschleife planen. Hier ist wie:

let timer = Timer(timeInterval: 1.0, target: self, selector: #selector(fire), userInfo: nil, Repeats: true)

RunLoop.current.add(timer, forMode: .commonModes)

Die erste Codezeile erstellt eine Instanz von Timer mithilfe des Timer(…)-Initialisierers. Die zweite Codezeile fügt den Timer mithilfe des Eingabemodus .commonModes zur aktuellen Laufschleife hinzu. Kurz gesagt, teilt dies der Ausführungsschleife mit, dass der Timer auf alle „üblichen“ Eingabemodi überprüft werden soll, wodurch der Timer effektiv während der Interaktion mit der Benutzeroberfläche ausgelöst werden kann.

Eine häufige Ursache für Frustration ist übrigens die versehentliche Verwendung des Timer(…)-Initialisierers, wenn Sie Timer.scheduledTimer(…) zum Erstellen eines Timers verwenden wollten. Wenn Sie Ersteres verwenden, wird der Timer erst ausgelöst, wenn Sie ihn einer Laufschleife hinzugefügt haben! Und Sie werden sich die Haare raufen, weil Sie sicher sind, dass Sie den Timer richtig eingestellt haben …

Ein häufiges Szenario in der praktischen iOS-Entwicklung besteht darin, dass Code mit einer kleinen Verzögerung ausgeführt wird. Für diesen Zweck ist es am einfachsten, Grand Central Dispatch zu verwenden und keinen Timer zu verwenden.

Der folgende Code führt eine Aufgabe im Hauptthread mit einer Verzögerung von 300 Millisekunden aus:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
print(“BOOYAH!”)
}

Es ist prägnanter als die Verwendung von Timer. Und da Sie es nur einmal ausführen, ist es ohnehin nicht nötig, einen Timer im Auge zu behalten.

So, jetzt wissen Sie es! Wenn Ihr Code zeitnah ausgeführt werden muss, ist Timer ein Tool, auf das Sie sich verlassen können. Geben oder nehmen 😉

Table of Contents