dispatch_after in Swift: Wie GCD verzögerte Aufgaben unter der Haube ausführt
Stellen Sie sich vor: Sie schreiben eine App in Swift und müssen einen Codeabschnitt nicht sofort, sondern beispielsweise nach 3 Sekunden ausführen. Das Erste, was einem in den Sinn kommt, ist dispatch_after. Aber wenn Sie in ältere Versionen von Swift oder auf Stack Overflow schauen, werden Sie sehen, dass sich diese Methode geändert hat. Und vor allem: Nur wenige verstehen, was tatsächlich innerhalb von Grand Central Dispatch (GCD) passiert, wenn wir sagen: „Mach das in 2 Sekunden“.
In diesem Artikel zeigen wir nicht nur, wie man dispatch_after im modernen Swift schreibt. Wir tauchen unter die Haube von GCD, analysieren Warteschlangen, Threads und Timer, damit Sie zuverlässigen und effizienten asynchronen Code schreiben können. Los geht's!
Was ist dispatch_after und warum wurde es zu asyncAfter?
In älteren Versionen von Swift (1.x, 2.x) konnten Sie auf folgenden Code stoßen:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2 * NSEC_PER_SEC)), dispatch_get_main_queue()) { print("2 Sekunden sind vergangen")}Dieser Code funktionierte, wirkte aber umständlich. Ab Swift 3 hat Apple die gesamte GCD-API überarbeitet und sie „swiftiger“ gemacht. Jetzt sieht die gleiche Funktionalität so aus:
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { print("2 Sekunden sind vergangen")}Beachten Sie: asyncAfter ist eine Methode der Warteschlange (DispatchQueue), keine separate Funktion. Dies ist eine wesentliche Änderung: Sie geben jetzt explizit an, in welcher Warteschlange die Aufgabe ausgeführt werden soll.
Wie GCD unter der Haube arbeitet: Warteschlangen, Threads und Timer
Um dispatch_after (oder asyncAfter) zu verstehen, müssen Sie drei Entitäten verstehen:
- DispatchQueue – eine Aufgabenwarteschlange. Kann seriell (serial) oder parallel (concurrent) sein.
- DispatchWorkItem – die Aufgabe, die Sie in die Warteschlange stellen. In unserem Fall ist das ein Closure.
- DispatchTime – der Zeitpunkt, zu dem die Aufgabe gestartet werden soll. GCD verwendet einen System-Timer, um Fristen zu verfolgen.
Wenn Sie asyncAfter aufrufen, erstellt GCD keinen neuen Thread. Stattdessen:
- Erstellt es einen Timer, der zum angegebenen Zeitpunkt ausgelöst wird.
- Legt es die Aufgabe in eine spezielle Warteschlange für verzögerte Aufgaben (oft als dispatch timer bezeichnet).
- Wenn der Timer auslöst, verschiebt GCD die Aufgabe in die normale
DispatchQueue, wo sie auf ihren Thread wartet.
Wenn die Warteschlange, in die Sie die Aufgabe stellen, mit anderen Aufgaben beschäftigt ist, kann Ihre verzögerte Aufgabe später als die angegebene Frist ausgeführt werden. Dies ist ein wichtiger Punkt: dispatch_after garantiert, dass die Aufgabe nicht früher als die Frist ausgeführt wird, aber sie kann später ausgeführt werden.
Praktisches Beispiel: dispatch_after zum Aktualisieren der Benutzeroberfläche
Das häufigste Szenario ist die Aktualisierung der Benutzeroberfläche einige Zeit nach einer Benutzeraktion. Angenommen, Sie zeigen eine Meldung „Gespeichert“ an und blenden sie nach 2 Sekunden aus:
func showTemporaryMessage() { let messageLabel = UILabel() messageLabel.text = "Gespeichert!" messageLabel.alpha = 1.0 view.addSubview(messageLabel)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { UIView.animate(withDuration: 0.5) { messageLabel.alpha = 0.0 } completion: { _ in messageLabel.removeFromSuperview() } }}Beachten Sie: Wir verwenden DispatchQueue.main, da alle UI-Operationen im Hauptthread ausgeführt werden müssen. Wenn wir asyncAfter in einer Hintergrundwarteschlange aufrufen und dann versuchen würden, die UI zu ändern, würde die App abstürzen.
Fallstricke von dispatch_after: Speicherlecks und Erfassen von self
Einer der häufigsten Fehler ist eine starke Referenz auf self im