585 Stimmen

Dispatch_after - GCD in Swift? dispatch_after - GCD in Swift?

Ich habe das iBook von Apple durchgesehen und konnte keine Definition davon finden:

Kann jemand die Struktur von dispatch_after erklären?

dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)

1114voto

matt Punkte 481428

Ich verwende dispatch_after so oft, dass ich eine Top-Level-Hilfsfunktion geschrieben habe, um die Syntax zu vereinfachen:

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Und jetzt kannst du so sprechen:

delay(0.4) {
    // etwas machen
}

Wow, eine Sprache, in der du die Sprache verbessern kannst. Was könnte besser sein?


Update für Swift 3, Xcode 8 Seed 6

Scheint fast nicht mehr wert zu sein, sich damit zu befassen, jetzt wo sie die Aufrufsyntax verbessert haben:

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

767voto

Cezary Wojcik Punkte 21665

Eine klarere Vorstellung von der Struktur:

dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)

dispatch_time_t ist ein UInt64. Der dispatch_queue_t ist tatsächlich als Alias für ein NSObject definiert, aber Sie sollten einfach Ihre vertrauten GCD-Methoden verwenden, um Queues zu erhalten. Der Block ist ein Swift Closure. Speziell ist dispatch_block_t definiert als () -> Void, was äquivalent zu () -> () ist.

Beispielverwendung:

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    print("test")
}

BEARBEITEN:

Ich empfehle die Verwendung von @matt's wirklich schöner delay Funktion.

BEARBEITEN 2:

In Swift 3 wird es neue Wrapper für GCD geben. Siehe hier: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

Das ursprüngliche Beispiel würde in Swift 3 wie folgt geschrieben werden:

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
    print("test")
}

Beachten Sie, dass Sie die Deklaration von deadlineTime als DispatchTime.now() + 1.0 schreiben können und das gleiche Ergebnis erhalten, da der + Operator wie folgt überschrieben ist (ähnlich für -):

  • func +(time: DispatchTime, seconds: Double) -> DispatchTime
  • func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime

Dies bedeutet, dass wenn Sie das DispatchTimeInterval enum nicht verwenden und einfach eine Zahl schreiben, angenommen wird, dass Sie Sekunden verwenden.

157voto

brindy Punkte 4186

Swift 3+

Dies ist super einfach und elegant in Swift 3+:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
    // ...
}

Ältere Antwort:

Um Cezarys Antwort zu erweitern, die nach 1 Nanosekunde ausgeführt wird, musste ich Folgendes tun, um nach 4,5 Sekunden auszuführen.

let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)

Bearbeitung: Ich habe festgestellt, dass mein originaler Code leicht falsch war. Implizites Typing führt zu einem Kompilierfehler, wenn Sie NSEC_PER_SEC nicht in ein Double konvertieren.

Wenn jemand eine optimale Lösung vorschlagen kann, wäre ich daran interessiert.

84voto

Waam Punkte 941

Matt's Syntax ist sehr gut und wenn Sie den Block ungültig machen müssen, möchten Sie vielleicht dies verwenden:

typealias dispatch_cancelable_closure = (cancel : Bool) -> Void

func delay(time:NSTimeInterval, closure:()->Void) ->  dispatch_cancelable_closure? {

    func dispatch_later(clsr:()->Void) {
        dispatch_after(
            dispatch_time(
                DISPATCH_TIME_NOW,
                Int64(time * Double(NSEC_PER_SEC))
            ),
            dispatch_get_main_queue(), clsr)
    }

    var closure:dispatch_block_t? = closure
    var cancelableClosure:dispatch_cancelable_closure?

    let delayedClosure:dispatch_cancelable_closure = { cancel in
        if closure != nil {
            if (cancel == false) {
                dispatch_async(dispatch_get_main_queue(), closure!);
            }
        }
        closure = nil
        cancelableClosure = nil
    }

    cancelableClosure = delayedClosure

    dispatch_later {
        if let delayedClosure = cancelableClosure {
            delayedClosure(cancel: false)
        }
    }

    return cancelableClosure;
}

func cancel_delay(closure:dispatch_cancelable_closure?) {

    if closure != nil {
        closure!(cancel: true)
    }
}

Verwendung wie folgt

let retVal = delay(2.0) {
    println("Später")
}
delay(1.0) {
    cancel_delay(retVal)
}

Credits

_Der Link oben scheint nicht zu funktionieren. Ursprünglicher Objc-Code von Github_

32voto

Vakas Punkte 5840

Einfachste Lösung in Swift 3.0 & Swift 4.0 & Swift 5.0

func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { 
        completion()
    }
}

Verwendung

delayWithSeconds(1) {
   //Mache etwas
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X