Jak napisać dispatch_after po GCD w Swift 3, 4 i 5?


445

W Swift 2 mogłem użyć dispatch_afterdo opóźnienia akcji za pomocą wielkiej centralnej wysyłki:

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 
})

Ale wydaje się, że nie kompiluje się już od Swift 3. Jaki jest preferowany sposób pisania tego we współczesnym Swift?


6
Więcej informacji na temat procesu migracji można znaleźć tutaj: https://swift.org/migration-guide/ W przypadku tego pytania istotna jest sekcja „Wysyłka”
tonik12

powinno być twoje pytanie UInt64?
Kochanie,

Odpowiedzi:


1125

Składnia jest po prostu:

// to run something in 0.1 seconds

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

Zauważ, że powyższa składnia dodawania secondsjako a Doublewydaje się być źródłem nieporozumień (szczególnie, że byliśmy przyzwyczajeni do dodawania nsec). Ta Doubleskładnia „dodaj sekundy jako ” działa, ponieważ deadlinejest DispatchTimea, za kulisami istnieje +operator, który zajmie Doublei doda tyle sekund do DispatchTime:

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

Ale jeśli naprawdę chcesz dodać liczbę całkowitą msec, μs lub nsec do DispatchTime, możesz również dodać a DispatchTimeIntervaldo DispatchTime. Oznacza to, że możesz:

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")
}

Wszystko to działa bezproblemowo dzięki oddzielnej metodzie przeciążenia dla +operatora w DispatchTimeklasie.

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

Zapytano, jak można anulować wysłane zadanie. Aby to zrobić, użyj DispatchWorkItem. Na przykład uruchamia to zadanie, które zostanie uruchomione za pięć sekund, lub jeśli kontroler widoku zostanie zwolniony i cofnięty, deinitanuluje zadanie:

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() { ... }

}

Zwróć uwagę na użycie [weak self]listy przechwytywania w DispatchWorkItem. Jest to niezbędne, aby uniknąć silnego cyklu odniesienia. Zauważ również, że nie powoduje to zapobiegawczego anulowania, ale po prostu zatrzymuje uruchamianie zadania, jeśli jeszcze tego nie zrobiło. Ale jeśli już się zaczął, zanim napotka cancel()wywołanie, blok zakończy wykonywanie (chyba że ręcznie sprawdzasz isCancelledwewnątrz bloku).


5
Dziękujemy za zwrócenie na to uwagi, a tak naprawdę swift.org/migration-guide wspomina o potrzebie ręcznego wprowadzenia tej zmiany.
mat

1
Przepraszam. Jest tu za późno :). Myślałem, że cały bałagan powinien odejść, ale nie skoczyłem. IMO „proste” rozwiązanie to jedno-prawdziwe-rozwiązanie.
tobiasdm

1
@ Rob, jak mógłbym to zrobić? Dzięki.
kemicofa ghost

Ok, więc jak dodać dynamiczne oczekiwanie? Na przykład mam liczbę let: Float = 1.0. A .now () + .milliseconds (number) nie działa. Podobnie jak Double (liczba). Nie mogę tego rozgryźć.
Kjell

2
Te DispatchTimeIntervalinterpretacje, jak .millisecondswymagają Int. Ale jeśli dodam tylko sekundy, użyłbym Doublenp let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob

128

Swift 4:

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

Do czasu .seconds(Int), .microseconds(Int)a .nanoseconds(Int)może być również używany.


7
.millisecondsjest lepszy niż Double.
DawnSong

5
Bardzo dobrze. Uwaga dla innych: możesz także użyć dowolnej innej DispatchTimeIntervalwartości wyliczenia. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern

@RobMacEachern, dzięki, że to dobra sugestia, że ​​dodam ją do odpowiedzi.
Sverrisson

2
.milliseconds is better than Double. - Chcę to na koszulce;).
Chris Prince

58

Jeśli chcesz tylko włączyć funkcję opóźnienia

Swift 4 i 5

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

Możesz go używać w następujący sposób:

delay(interval: 1) { 
    print("Hi!")
}

DispatchQueue.main.asyncAfter (termin:) nie działa. Mówi, że nie przeciąża żadnej metody ze swojej nadklasy.
Fabrizio Bartolomucci

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)jest prostsze.
DawnSong

16

po wydaniu Swift 3 należy również dodać @escaping

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

5

Nieco inny smak odpowiedzi zaakceptowanej.

Szybki 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)")
 }

5

Szybki 4

Możesz utworzyć rozszerzenie na DispatchQueue i dodać opóźnienie funkcji, które DispatchQueuewewnętrznie używa funkcji asyncAfter

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

I użyć

DispatchQueue.delay(.milliseconds(10)) {
   print("task to be done")
}

2
Czym różni się to od odpowiedzi @ rockdaswift?
brandonscript

jak wspomniałem, otacza asyncAfter wewnątrz funkcji performAfter, która przyjmuje parametr jako opóźnienie i łatwiej jest wywołać go za pomocą performAfter (opóźnienie: 2) {}
Suhit Patil

Domyślnie parametry zamknięcia nie są ucieczkowe, @escaping wskazuje, że parametr zamknięcia może się wydostać. dodano @ parametr ucieczki w zamknięciu, aby zapisać potencjalną awarię.
Suhit Patil

3

połączenie DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Zdecydowanie polecam użycie narzędzi Xcode do konwersji na Swift 3 (Edycja> Konwertuj> Na bieżącą składnię Swift). Złapało to dla mnie


3

W Swift 4.1 i Xcode 9.4.1

Prosta odpowiedź to ...

//To call function after 5 seconds time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}

3
Nie wiesz, jak to się różni od przyjętej odpowiedzi?
brandonscript

3

Swift 5 i wyżej

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

1

Żadna z wymienionych odpowiedzi nie działa w wątku innym niż główny, więc dodanie moich 2 centów.

W głównej kolejce (główny wątek)

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

LUB

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

W kolejce globalnej (nie główny wątek, na podstawie podanego QOS)

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

LUB

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

0

To działało dla mnie w Swift 3

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds


DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

5
Nie wiesz, jak to się różni od przyjętej odpowiedzi?
brandonscript

0

Możesz użyć

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

0

Spróbuj tego

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }

Nie wiesz, jak to się różni od odpowiedzi, której dotyczy problem?
brandonscript
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.