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?

brandonscript
la source
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).

Rob
la source
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é.

Sverrisson
la source
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!")
}
rockdaswift
la source
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()
  }
}
Marco Pappalardo
la source
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)")
 }
Md. Ibrahim Hassan
la source
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")
}
Suhit Patil
la source
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

jjatie
la source
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
}
iOS
la source
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                 
})
midhun p
la source
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) {
    // ...
}
MANN
la source
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)")
}
Garçon Ankit
la source
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
    }
Parth Dhorda
la source
0

essaye ça

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
la source
Vous ne savez pas en quoi cela diffère de la réponse concernée?
brandonscript