584 votes

dispatch_after - GCD en Swift ?

Je suis passé par le iBook d'Apple, et je n'ai pas trouvé de définition de ce terme :

Quelqu'un peut-il expliquer la structure de dispatch_after ?

dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)

1 votes

Apple a dépublié ce livre en 2018. Les dernières archives que j'ai pu trouver datent de décembre 2017. . Les anciens liens vers l'iBook redirigent maintenant simplement vers developer.apple.com/documentation/swift .

1114voto

matt Points 60113

J'utilise dispatch_after si souvent que j'ai écrit une fonction utilitaire de haut niveau pour rendre la syntaxe plus simple :

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Et maintenant tu peux parler comme ça :

delay(0.4) {
    // do stuff
}

Wow, une langue où vous pouvez améliorer la langue. Qu'est-ce qui pourrait être mieux ?


Mise à jour pour Swift 3, Xcode 8 Seed 6

Il semble que cela ne vaille presque plus la peine de s'en préoccuper, maintenant qu'ils ont amélioré la syntaxe d'appel :

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

2 votes

J'avais juste besoin d'un raccourci pour le calcul du délai, j'ai fini par le faire : func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }

0 votes

Serait-ce delay(0.0) { /*stuff*/ } (insérer les retours à la ligne appropriés) serait une commande valide si je voulais faire quelque chose sur le fil principal sans aucun délai (comme modifier l'interface graphique) ?

1 votes

@Matt You tell me. Tu regardes le code. Quel est le problème ? Ou si vous avez un doute, appelez dispatch_async sur le fil principal vous-même. Encore une fois, quel est le problème ?

767voto

Cezary Wojcik Points 2886

Une idée plus claire de la structure :

dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)

dispatch_time_t est un UInt64 . Le site dispatch_queue_t est en fait un type aliasé à un NSObject mais vous devriez simplement utiliser vos méthodes GCD habituelles pour obtenir des files d'attente. Le bloc est une fermeture Swift. Plus précisément, dispatch_block_t est défini comme suit () -> Void ce qui est équivalent à () -> () .

Exemple d'utilisation :

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    print("test")
}

EDITAR:

Je recommande d'utiliser @matt est vraiment sympa delay fonction .

EDIT 2 :

Dans Swift 3, il y aura de nouveaux wrappers pour GCD. Voir ici : https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

L'exemple original serait écrit comme suit dans Swift 3 :

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
    print("test")
}

Notez que vous pouvez écrire le deadlineTime déclaration comme DispatchTime.now() + 1.0 et obtenir le même résultat car le + est surchargé comme suit (de la même manière pour - ) :

  • func +(time: DispatchTime, seconds: Double) -> DispatchTime
  • func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime

Cela signifie que si vous n'utilisez pas l'option DispatchTimeInterval enum et que vous écrivez simplement un nombre, il est supposé que vous utilisez les secondes.

18 votes

Conseil : comme le bloc est le dernier paramètre de la fonction, vous pouvez utiliser la syntaxe de la "fermeture arrière" de Swift pour une meilleure lisibilité : dispatch_after(1, dispatch_get_main_queue()) { println("test") }

8 votes

Je pense qu'utiliser le numéro 1 en dispatch_after(1, ... peut causer beaucoup de confusion ici. Les gens penseront qu'il s'agit d'un nombre de secondes, alors qu'il s'agit en fait de nano-seconde . Je suggère de voir la réponse de @brindy sur la façon de créer ce numéro correctement.

4 votes

Veuillez changer 1 a dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))) parce que cela mène à la confusion. Les gens pourraient penser qu'il n'est pas nécessaire de créer un dispatch_time_t en Swift

156voto

brindy Points 1884

Swift 3+

C'est très facile et élégant en Swift 3+ :

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
    // ...
}

Réponse plus ancienne :

Pour développer la réponse de Cezary, qui s'exécute après 1 nanoseconde, j'ai dû faire ce qui suit pour exécuter après 4 secondes et demie.

let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)

Edit : J'ai découvert que mon code original était légèrement erroné. Le typage implicite provoque une erreur de compilation si vous ne transformez pas NSEC_PER_SEC en un Double.

Si quelqu'un peut suggérer une solution plus optimale, je serais ravi de l'entendre.

0 votes

Je reçois une erreur de compilation pour une API dépréciée avec dispatch_get_current_queue() . J'ai utilisé dispatch_get_main_queue() à la place.

0 votes

@DavidL - merci, dispatch_get_main_queue() est définitivement ce que vous devriez utiliser. Je mettrai à jour.

0 votes

J'ai essayé ceci dans un terrain de jeu avec swift 3 et cela ne fonctionne pas

84voto

Waam Points 131

la syntaxe de matt est très agréable et si vous avez besoin d'invalider le bloc, vous pouvez l'utiliser :

typealias dispatch_cancelable_closure = (cancel : Bool) -> Void

func delay(time:NSTimeInterval, closure:()->Void) ->  dispatch_cancelable_closure? {

    func dispatch_later(clsr:()->Void) {
        dispatch_after(
            dispatch_time(
                DISPATCH_TIME_NOW,
                Int64(time * Double(NSEC_PER_SEC))
            ),
            dispatch_get_main_queue(), clsr)
    }

    var closure:dispatch_block_t? = closure
    var cancelableClosure:dispatch_cancelable_closure?

    let delayedClosure:dispatch_cancelable_closure = { cancel in
        if closure != nil {
            if (cancel == false) {
                dispatch_async(dispatch_get_main_queue(), closure!);
            }
        }
        closure = nil
        cancelableClosure = nil
    }

    cancelableClosure = delayedClosure

    dispatch_later {
        if let delayedClosure = cancelableClosure {
            delayedClosure(cancel: false)
        }
    }

    return cancelableClosure;
}

func cancel_delay(closure:dispatch_cancelable_closure?) {

    if closure != nil {
        closure!(cancel: true)
    }
}

Utilisez comme suit

let retVal = delay(2.0) {
    println("Later")
}
delay(1.0) {
    cancel_delay(retVal)
}

crédits

_Le lien ci-dessus semble être en panne. Code original Objc de Github_

1 votes

La seule fonctionnalité de performance qui a performSelector:afterDelay est la possibilité de l'annuler. Seule cette solution couvre le problème. Merci

0 votes

@HotJard Notez que performSelector:afterDelay: est maintenant disponible dans Swift 2, vous pouvez donc l'annuler.

0 votes

@matt mais il n'est disponible que pour NSObject, n'est-ce pas ?

15voto

garafajon Points 110

Une autre façon est d'étendre Double comme ceci :

extension Double {
   var dispatchTime: dispatch_time_t {
       get {
           return dispatch_time(DISPATCH_TIME_NOW,Int64(self * Double(NSEC_PER_SEC)))
       }
   }
}

Alors vous pouvez l'utiliser comme ceci :

dispatch_after(Double(2.0).dispatchTime, dispatch_get_main_queue(), { () -> Void in
            self.dismissViewControllerAnimated(true, completion: nil)
    })

J'aime la fonction de retardement de Matt mais, par préférence, je préfère limiter les fermetures de passage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X