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?

brandonscript
źródło
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).

Obrabować
źródło
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.

Sverrisson
źródło
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!")
}
Rockdaswift
źródło
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()
  }
}
Marco Pappalardo
źródło
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)")
 }
Pan Ibrahim Hassan
źródło
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")
}
Suhit Patil
źródło
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

jjatie
źródło
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
}
iOS
źródło
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                 
})
midhun p
źródło
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) {
    // ...
}
MANN
źródło
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)")
}
Anargit garg
źródło
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
    }
Parth Dhorda
źródło
0

Spróbuj tego

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
źródło
Nie wiesz, jak to się różni od odpowiedzi, której dotyczy problem?
brandonscript