Comment puis-je écrire dispatch_after GCD dans Swift 3, 4 et 5?


445

Dans Swift 2, j'ai pu utiliser dispatch_afterpour retarder une action à l'aide de Grand Central Dispatch:

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

Mais cela ne semble plus se compiler depuis Swift 3. Quelle est la meilleure façon d'écrire ceci dans Swift moderne?


6
De plus amples informations sur le processus de migration peuvent être trouvées ici: https://swift.org/migration-guide/ La section "Dispatch" est pertinente pour cette question
tonik12

devrait être votre question UInt64?
Honey

Réponses:


1125

La syntaxe est simplement:

// to run something in 0.1 seconds

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

Notez que la syntaxe ci-dessus de l'ajout en secondstant que a Doublesemble être une source de confusion (d'autant plus que nous étions habitués à ajouter nsec). Cette Doublesyntaxe «ajouter des secondes en tant que » fonctionne car il deadlines'agit d'un DispatchTimeet, dans les coulisses, il existe un +opérateur qui prendra un Doubleet ajoutera autant de secondes au DispatchTime:

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

Mais, si vous voulez vraiment ajouter un nombre entier de ms, μs ou nsec au DispatchTime, vous pouvez également ajouter un DispatchTimeIntervalà un DispatchTime. Cela signifie que vous pouvez faire:

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

Tout cela fonctionne parfaitement grâce à cette méthode de surcharge distincte pour l' +opérateur de la DispatchTimeclasse.

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

Il a été demandé comment procéder pour annuler une tâche envoyée. Pour ce faire, utilisez DispatchWorkItem. Par exemple, cela démarre une tâche qui se déclenchera dans cinq secondes, ou si le contrôleur de vue est rejeté et désalloué, deinitil annulera la tâche:

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

}

Notez l'utilisation de la [weak self]liste de capture dans le DispatchWorkItem. Ceci est essentiel pour éviter un cycle de référence fort. Notez également que cela n'effectue pas une annulation préventive, mais arrête simplement la tâche de démarrer si ce n'est pas déjà fait. Mais s'il a déjà commencé au moment où il rencontre l' cancel()appel, le bloc terminera son exécution (sauf si vous vérifiez manuellement l' isCancelledintérieur du bloc).


5
Merci de l'avoir signalé, et en fait, swift.org/migration-guide mentionne la nécessité de faire ce changement à la main.
mat

1
Oh pardon. Il est trop tard ici :). Je pensais que tout le gâchis devrait disparaître, mais je n'ai pas fait le saut. OMI, la solution "simple" est la seule solution.
tobiasdm

1
@Rob, comment pourrais-je l'annuler? Merci.
kemicofa ghost

Ok alors comment ajouter une attente dynamique? Par exemple, j'ai un numéro let: Float = 1.0. Et .now () + .millisecondes (nombre) ne fonctionne pas. Pas plus que Double (nombre). Je ne peux pas comprendre.
Kjell

2
Les DispatchTimeIntervalrendus, comme l' .millisecondsexigent Int. Mais si juste ajouter des secondes, j'utiliserais Double, par exemple let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob

128

Swift 4:

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

Pour le moment .seconds(Int), .microseconds(Int)et .nanoseconds(Int)peut également être utilisé.


7
.millisecondsest meilleur que Double.
DawnSong

5
Très agréable. Remarque pour les autres: vous pouvez également utiliser n'importe laquelle des autres DispatchTimeIntervalvaleurs d'énumération. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern

@RobMacEachern, merci c'est une bonne suggestion, je l'ajoute à la réponse.
Sverrisson

2
.milliseconds is better than Double. - Je veux ça sur un T-shirt;).
Chris Prince

58

Si vous voulez juste que la fonction de retard

Swift 4 et 5

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

Vous pouvez l'utiliser comme:

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

DispatchQueue.main.asyncAfter (délai:) ne fonctionne pas. Il dit qu'il ne surcharge aucune méthode de sa superclasse.
Fabrizio Bartolomucci

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)est plus simple.
DawnSong

16

après la sortie de Swift 3, le @escaping doit également être ajouté

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

5

Une saveur quelque peu différente de la réponse acceptée.

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

Vous pouvez créer une extension sur DispatchQueue et ajouter un retard de fonction qui utilise la DispatchQueuefonction asyncAfter en interne

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

et utilise

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

2
En quoi est-ce différent de la réponse de @ rockdaswift?
brandonscript

comme je l'ai mentionné, il encapsule asyncAfter dans la fonction performAfter qui prend le délai comme paramètre et il peut être plus facile d'appeler en utilisant simplement performAfter (delay: 2) {}
Suhit Patil

Les paramètres de fermeture ne s'échappent pas par défaut, @escaping indique qu'un paramètre de fermeture peut s'échapper. ajout d'un paramètre @ escape à la fermeture pour éviter un crash potentiel.
Suhit Patil

3

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

Je recommande fortement d'utiliser les outils Xcode pour convertir en Swift 3 (Édition> Convertir> En syntaxe Swift actuelle). Il a attrapé ça pour moi


3

Dans Swift 4.1 et Xcode 9.4.1

La réponse simple est ...

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

3
Vous ne savez pas en quoi cela est différent de la réponse acceptée?
brandonscript

3

Swift 5 et supérieur

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

1

Aucune des réponses n'a mentionné de s'exécuter sur un thread non principal, ajoutant donc mes 2 cents.

Sur la file d'attente principale (thread principal)

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

OU

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

Sur file d'attente globale (thread non principal, basé sur QOS spécifié).

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

OU

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

0

Cela a fonctionné pour moi dans 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
Vous ne savez pas en quoi cela diffère de la réponse acceptée?
brandonscript

0

Vous pouvez utiliser

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

0

essaye ça

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

Vous ne savez pas en quoi cela diffère de la réponse concernée?
brandonscript
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.