Wie schreibe ich dispatch_after GCD in Swift 3, 4 und 5?

445

In Swift 2 konnte ich dispatch_aftereine Aktion mit Grand Central Dispatch verzögern:

var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) 
dispatch_after(dispatchTime, dispatch_get_main_queue(), { 
    // your function here 
})

Dies scheint jedoch seit Swift 3 nicht mehr zu kompilieren. Was ist die bevorzugte Methode, um dies im modernen Swift zu schreiben?

Brandonscript
quelle
6
Weitere Informationen zum Migrationsprozess finden Sie hier: https://swift.org/migration-guide/ Der Abschnitt "Versand" ist für diese Frage relevant
tonik12
sollte Ihre Frage sein UInt64?
Honig

Antworten:

1125

Die Syntax lautet einfach:

// to run something in 0.1 seconds

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    // your code here
}

Beachten Sie, dass die obige Syntax des Hinzufügens secondsals eine DoubleQuelle der Verwirrung zu sein scheint (insbesondere, da wir es gewohnt waren, ns hinzuzufügen). Diese DoubleSyntax "Sekunden hinzufügen als " funktioniert, weil a deadlineist DispatchTimeund hinter den Kulissen gibt es einen +Operator, der a benötigt Doubleund so viele Sekunden zum DispatchTime:

public func +(time: DispatchTime, seconds: Double) -> DispatchTime

Wenn Sie jedoch wirklich eine Ganzzahl von ms, μs oder ns DispatchTimehinzufügen möchten , können Sie auch a DispatchTimeIntervalzu a hinzufügen DispatchTime. Das heißt, Sie können:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
    os_log("500 msec seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(1_000_000)) {
    os_log("1m μs seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .nanoseconds(1_500_000_000)) {
    os_log("1.5b nsec seconds later")
}

Diese funktionieren aufgrund dieser separaten Überladungsmethode für den +Operator in der DispatchTimeKlasse nahtlos .

public func +(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime

Es wurde gefragt, wie man eine versendete Aufgabe storniert. Verwenden Sie dazu DispatchWorkItem. Dadurch wird beispielsweise eine Aufgabe gestartet, die in fünf Sekunden ausgelöst deinitwird. Wenn der Ansichtscontroller geschlossen und freigegeben wird, wird die Aufgabe abgebrochen :

class ViewController: UIViewController {

    private var item: DispatchWorkItem?

    override func viewDidLoad() {
        super.viewDidLoad()

        item = DispatchWorkItem { [weak self] in
            self?.doSomething()
            self?.item = nil
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: item!)
    }

    deinit {
        item?.cancel()
    }

    func doSomething() { ... }

}

Beachten Sie die Verwendung der [weak self]Erfassungsliste in der DispatchWorkItem. Dies ist wichtig, um einen starken Referenzzyklus zu vermeiden. Beachten Sie auch, dass dies keine vorbeugende Stornierung bewirkt, sondern nur den Start der Aufgabe stoppt, falls dies noch nicht geschehen ist. Wenn es jedoch bereits gestartet ist, als es auf den cancel()Aufruf trifft , beendet der Block seine Ausführung (es sei denn, Sie überprüfen manuell isCancelledinnerhalb des Blocks).

rauben
quelle
5
Vielen Dank, dass Sie darauf hingewiesen haben, und in der Tat erwähnt swift.org/migration-guide die Notwendigkeit, diese Änderung von Hand vorzunehmen.
Matt
1
Oh, das tut mir leid. Hier ist es viel zu spät :). Dachte, dass das ganze Chaos eigentlich gehen sollte, aber machte nicht den Sprung. IMO ist die "einfache" Lösung die einzig wahre Lösung.
Tobiasdm
1
@Rob wie würde ich vorgehen, um es abzubrechen? Vielen Dank.
Kemicofa Ghost
Ok, wie fügt man ein dynamisches Warten hinzu? Zum Beispiel habe ich eine Let-Nummer: Float = 1.0. Und .now () + .milliseconds (number) funktioniert nicht. Double (Nummer) auch nicht. Ich kann es nicht herausfinden.
Kjell
2
Die DispatchTimeIntervalWiedergaben .millisecondserfordern wie Int. Aber wenn ich nur Sekunden hinzufüge, würde ich Doublez let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob
128

Swift 4:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
   // Code
}

Für die Zeit .seconds(Int), .microseconds(Int)und .nanoseconds(Int)kann auch verwendet werden.

Sverrisson
quelle
7
.millisecondsist besser als Double.
DawnSong
5
Sehr schön. Ein Hinweis für andere: Sie können auch andere DispatchTimeIntervalAufzählungswerte verwenden. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern
@RobMacEachern, danke, das ist ein guter Vorschlag, den ich der Antwort hinzufüge.
Sverrisson
2
.milliseconds is better than Double. - Ich will das auf einem T-Shirt;).
Chris Prince
58

Wenn Sie nur die Verzögerungsfunktion in wollen

Swift 4 & 5

func delay(interval: TimeInterval, closure: @escaping () -> Void) {
     DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
          closure()
     }
}

Sie können es verwenden wie:

delay(interval: 1) { 
    print("Hi!")
}
rockdaswift
quelle
DispatchQueue.main.asyncAfter (Deadline :) funktioniert nicht. Es heißt, dass es keine Methode aus seiner Oberklasse überlädt.
Fabrizio Bartolomucci
7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)ist einfacher.
DawnSong
16

Nach der Veröffentlichung von Swift 3 muss auch das @escaping hinzugefügt werden

func delay(_ delay: Double, closure: @escaping () -> ()) {
  DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    closure()
  }
}
Marco Pappalardo
quelle
5

Ein etwas anderer Geschmack der akzeptierten Antwort.

Swift 4

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 + .milliseconds(500) + 
.microseconds(500) + .nanoseconds(1000)) {
                print("Delayed by 0.1 second + 500 milliseconds + 500 microseconds + 
                      1000 nanoseconds)")
 }
Md. Ibrahim Hassan
quelle
5

Swift 4

Sie können eine Erweiterung für DispatchQueue erstellen und eine Funktionsverzögerung hinzufügen, die DispatchQueueintern die asyncAfter-Funktion verwendet

extension DispatchQueue {
   static func delay(_ delay: DispatchTimeInterval, closure: @escaping () -> ()) {
      DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: closure)
   }
}

und verwenden

DispatchQueue.delay(.milliseconds(10)) {
   print("task to be done")
}
Suhit Patil
quelle
2
Wie unterscheidet sich das von der Antwort von @ rockdaswift?
Brandonscript
Wie bereits erwähnt, wird asyncAfter in die Funktion performAfter eingeschlossen, die die Verzögerung als Parameter verwendet, und es kann einfacher sein, sie nur mit performAfter (delay: 2)
aufzurufen.
Schließparameter sind standardmäßig nicht maskiert. @Scaping gibt an, dass ein Schließparameter möglicherweise maskiert wird. Beim Schließen wurde der Parameter @ Escape hinzugefügt, um einen möglichen Absturz zu vermeiden.
Suhit Patil
3

Anruf DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Ich würde dringend empfehlen, die Xcode-Tools zum Konvertieren in Swift 3 zu verwenden (Bearbeiten> Konvertieren> In aktuelle Swift-Syntax). Es hat das für mich gefangen

jjatie
quelle
3

In Swift 4.1 und Xcode 9.4.1

Einfache Antwort ist ...

//To call function after 5 seconds time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}
iOS
quelle
3
Sie sind sich nicht sicher, wie sich dies von der akzeptierten Antwort unterscheidet?
Brandonscript
3

Swift 5 und höher

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})
midhun p
quelle
1

Keine der genannten Antworten läuft auf einem Nicht-Haupt-Thread, also addiere ich meine 2 Cent.

In der Hauptwarteschlange (Hauptthread)

let mainQueue = DispatchQueue.main
let deadline = DispatchTime.now() + .seconds(10)
mainQueue.asyncAfter(deadline: deadline) {
    // ...
}

ODER

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(10)) { 
    // ...
}

In der globalen Warteschlange (kein Hauptthread, basierend auf der angegebenen QOS).

let backgroundQueue = DispatchQueue.global()
let deadline = DispatchTime.now() + .milliseconds(100)
backgroundQueue.asyncAfter(deadline: deadline, qos: .background) { 
    // ...
}

ODER

DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + .milliseconds(100), qos: .background) {
    // ...
}
MANN
quelle
0

Das hat bei mir in Swift 3 funktioniert

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds


DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}
Ankit garg
quelle
5
Nicht sicher, wie sich dies von der akzeptierten Antwort unterscheidet?
Brandonscript
0

Sie können verwenden

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(100)) {
        // Code
    }
Parth Dhorda
quelle
0

Versuche dies

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A. Guz
quelle
Sie sind sich nicht sicher, wie sich dies von der betroffenen Antwort unterscheidet?
Brandonscript