Come scrivo dispatch_after GCD in Swift 3, 4 e 5?


445

In Swift 2, sono stato in grado di utilizzare dispatch_afterper ritardare un'azione utilizzando la spedizione Grand Central:

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

Ma questo non sembra più compilarsi da Swift 3. Qual è il modo preferito di scrivere questo in Swift moderno?


6
Ulteriori informazioni sul processo di migrazione sono disponibili qui: https://swift.org/migration-guide/ La sezione "Spedizione" è pertinente per questa domanda
tonik12

dovrebbe essere la tua domanda UInt64?
Miele

Risposte:


1126

La sintassi è semplicemente:

// to run something in 0.1 seconds

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

Nota, la sintassi di cui sopra secondscome aggiunta Doublesembra essere una fonte di confusione (specialmente da quando eravamo abituati ad aggiungere nsec). La Doublesintassi "aggiungi secondi come " funziona perché deadlineè un DispatchTimee, dietro le quinte, c'è un +operatore che impiegherà un Doublee aggiungerà molti secondi a DispatchTime:

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

Ma, se vuoi davvero aggiungere un numero intero di msec, μs o nsec a DispatchTime, puoi anche aggiungere DispatchTimeIntervala a DispatchTime. Ciò significa che puoi fare:

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

Tutti questi funzionano perfettamente grazie a questo metodo di sovraccarico separato per l' +operatore della DispatchTimeclasse.

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

È stato chiesto come si fa a cancellare un compito inviato. Per fare questo, usa DispatchWorkItem. Ad esempio, questo avvia un'attività che si attiverà in cinque secondi, o se il controller di visualizzazione viene ignorato e deallocato, deinitannullerà l'attività:

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

}

Si noti l'uso [weak self]dell'elenco di acquisizione in DispatchWorkItem. Ciò è essenziale per evitare un forte ciclo di riferimento. Si noti inoltre che ciò non comporta una cancellazione preventiva, ma piuttosto interrompe l'avvio dell'attività se non lo è già. Ma se è già iniziato quando incontra la cancel()chiamata, il blocco finirà la sua esecuzione (a meno che tu non stia controllando manualmente isCancelledall'interno del blocco).


5
Grazie per averlo sottolineato, e in effetti swift.org/migration-guide menziona la necessità di apportare questo cambiamento a mano.
opaco

1
Oh scusa. È troppo tardi qui :). Pensavo che tutto il casino dovesse andare davvero, ma non ho fatto il salto. IMO la soluzione "semplice" è l'unica vera soluzione.
Tobiasdm,

1
@Rob come potrei fare per cancellarlo? Grazie.
kemicofa ghost

Ok, quindi come si aggiunge un'attesa dinamica? Ad esempio, ho un numero let: Float = 1.0. E .now () + .milliseconds (numero) non funziona. Nemmeno Double (numero). Non riesco a capirlo.
Kjell,

2
Le DispatchTimeIntervalinterpretazioni, come .millisecondsrichiedono Int. Ma se solo aggiungessi secondi, userei Double, ad es let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob,

128

Swift 4:

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

Per il momento .seconds(Int), .microseconds(Int)e .nanoseconds(Int)può anche essere usato.


7
.millisecondsè meglio del doppio.
DawnSong,

5
Molto bella. Una nota per gli altri: puoi anche usare uno qualsiasi degli altri DispatchTimeIntervalvalori enum. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern,

@RobMacEachern, grazie che è un buon suggerimento lo aggiungo alla risposta.
Sverrisson,

2
.milliseconds is better than Double. - Lo voglio su una maglietta;).
Chris Prince,

58

Se vuoi solo attivare la funzione di ritardo

Swift 4 e 5

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

Puoi usarlo come:

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

DispatchQueue.main.asyncAfter (deadline:) non funziona. Dice che non sovraccarica alcun metodo dalla sua superclasse.
Fabrizio Bartolomucci,

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)è più semplice.
DawnSong,

16

dopo il rilascio di Swift 3, è necessario aggiungere anche @escaping

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

5

Un sapore leggermente diverso della risposta accettata.

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

5

Swift 4

È possibile creare un'estensione su DispatchQueue e aggiungere un ritardo di funzione che utilizza DispatchQueueinternamente la funzione asyncAfter

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

e usare

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

2
In che cosa differisce dalla risposta di @ rockdaswift?
brandonscript,

come ho già detto, avvolge async Dopo la funzione performAfter che accetta il ritardo come parametro e può essere più semplice chiamare usando solo performAfter (delay: 2) {}
Suhit Patil

Per impostazione predefinita, i parametri di chiusura non eseguono l'escaping, @escaping indica che un parametro di chiusura può uscire. aggiunto il parametro @ escaping in chiusura per salvare il potenziale crash.
Suhit Patil,

3

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

Consiglio vivamente di usare gli strumenti Xcode per convertire in Swift 3 (Modifica> Converti> In sintassi Swift corrente). Mi ha preso questo


3

In Swift 4.1 e Xcode 9.4.1

La risposta semplice è ...

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

3
Non sei sicuro di come sia diverso dalla risposta accettata?
brandonscript il

3

Swift 5 e versioni successive

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

1

Nessuna delle risposte menzionate è in esecuzione su un thread non principale, quindi aggiungendo i miei 2 centesimi.

Sulla coda principale (thread principale)

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

O

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

Sulla coda globale (thread non principale, basato su QOS specificato).

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

O

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

0

Questo ha funzionato per me in 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
Non sei sicuro di come questo differisca dalla risposta accettata?
brandonscript

0

Puoi usare

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

0

prova questo

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

Non sei sicuro di come sia diverso dalla risposta interessata?
brandonscript
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.