web-dev-qa-db-fra.com

dispatch_after - GCD à Swift?

J'ai parcouru le iBook d'Apple et je n'ai trouvé aucune définition de celui-ci:

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

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

J'utilise dispatch_after si souvent que j'ai écrit une fonction utilitaire de niveau supérieur pour simplifier la syntaxe:

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, vous pouvez 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

Cela ne semble presque pas la peine de s'inquiéter, 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)
}
1073
matt

Pour développer la réponse de Cezary, qui s'exécutera après une nanoseconde, je devais faire ce qui suit pour l'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 d'origine était légèrement faux. Le typage implicite provoque une erreur de compilation si vous ne convertissez pas NSEC_PER_SEC en Double.

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

== Mise à jour pour Swift 3 ==

Ceci est super facile et élégant dans Swift 3:

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

La syntaxe de matt est très agréable et si vous devez invalider le bloc, vous pouvez utiliser ceci:

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

Utiliser 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 Objc original de Github

83
Waam

Apple a un fragment dispatch_after pour Objective-C :

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    <#code to be executed after a specified delay#>
});

Voici le même extrait porté sur Swift 3:

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + <#delayInSeconds#>) {
  <#code to be executed after a specified delay#>
}
20
Senseful

La solution la plus simple dans Swift 3.0 & Swift 4.0

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

Usage

delayWithSeconds(1) {
   //Do something
}
20
Vakas

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

Ensuite, 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 délai de matt, mais je préfère ne pas limiter les fermetures passagères.

14
garafajon

Dans Swift 3.0  

Files d'attente d'envoi

  DispatchQueue(label: "test").async {
        //long running Background Task
        for obj in 0...1000 {
            print("async \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.async(execute: { 
            print("UI update on main queue")
        })

    }

    DispatchQueue(label: "m").sync {
        //long running Background Task
        for obj in 0...1000 {
            print("sync \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.sync(execute: {
            print("UI update on main queue")
        })
    }

Envoi après 5 secondes

    DispatchQueue.main.after(when: DispatchTime.now() + 5) {
        print("Dispatch after 5 sec")
    }
8

Version Swift 3.0

La fonction de fermeture suivante exécute une tâche après le délai sur le thread principal. 

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Appelez cette fonction comme suit:

performAfterDelay(delay: 4.0) {
  print("test")
}
4
Himanshu Mahajan

1) Ajoutez cette méthode dans le cadre de UIViewController Extension.

extension UIViewController{
func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(time, dispatch_get_main_queue(), block)
    }
}

Appelez cette méthode sur VC: 

    self.runAfterDelay(5.0, block: {
     //Add code to this block
        print("run After Delay Success")
    })

2) performSelector ("votre nom de méthode", withObject: nil, afterDelay: 1)

3) 

override func viewWillAppear(animated: Bool) {

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue(), { () -> () in
    //Code Here
})

// forme compacte

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) {
    //Code here
}

}

4
A.G

Bien que ce ne soit pas la question initiale posée par le PO, certaines questions relatives à NSTimer ont été marquées comme des doublons de cette question. Il est donc utile d’inclure une réponse NSTimer ici.

NSTimer vs dispatch_after

  • NSTimer est plus élevé, alors que dispatch_after est plus faible.
  • NSTimer est plus facile à annuler. Annuler dispatch_after nécessite d’écrire plus de code .

Retarder une tâche avec NSTimer

Créez une instance NSTimer.

var timer = NSTimer()

Démarrez le chronomètre avec le délai dont vous avez besoin.

// invalidate the timer if there is any chance that it could have been called before
timer.invalidate()
// delay of 2 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 

Ajoutez une fonction à appeler après le délai (en utilisant le nom que vous avez utilisé pour le paramètre selector ci-dessus).

func delayedAction() {
    print("Delayed action has now started."
}

Remarques

  • Si vous devez annuler l'action avant qu'elle ne se produise, appelez simplement timer.invalidate().
  • Pour une action répétée, utilisez repeats: true
  • Si vous avez un événement ponctuel sans qu'il soit nécessaire d'annuler, il n'est pas nécessaire de créer la variable d'instance timer. Ce qui suit suffira:

    NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 
    
  • Voir ma réponse plus complète ici .

3
Suragch

Utilisez cette fonction pour plusieurs fonctions. Ceci est très utile pour utiliser des animations ou Activity Loader pour des fonctions statiques ou toute mise à jour de l'interface utilisateur.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
            // Call your function 1
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                // Call your function 2
            }
        }

Par exemple - Utilisez une animation avant un rechargement tableView. Ou toute autre mise à jour de l'interface utilisateur après l'animation.

*// Start your amination* 
self.startAnimation()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
                *// The animation will execute depending on the delay time*
                self.stopAnimation()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    *// Now update your view*
                     self.fetchData()
                     self.updateUI()
                }
            }
2
Rahul Singha Roy

Swift 3 & 4:

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

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

utilisation: 

DispatchQueue.delay(.seconds(1)) {
    print("This is after delay")
}
2
Suhit Patil

Retarder un appel GCD avec asyncAfter dans Swift

let delayQueue = DispatchQueue(label: "com.theappmaker.in", qos: .userInitiated)
let additionalTime: DispatchTimeInterval = .seconds(2)

Nous pouvons retarder en tant que ** microsecondes , millisecondes , nanosecondes

delayQueue.asyncAfter(deadline: .now() + 0.60) {
    print(Date())
}

delayQueue.asyncAfter(deadline: .now() + additionalTime) {
    print(Date())
}
1
Sanjay Mali

Je préfère toujours utiliser l'extension au lieu de fonctions libres.

Swift 4 

public extension DispatchQueue {

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

  class func performAction(after seconds: TimeInterval, callBack: @escaping (() -> Void) ) {
    DispatchQueue.delay(delay: seconds) {
      callBack()
    }
  }

}

Utilisez comme suit.

DispatchQueue.performAction(after: 0.3) {
  // Code Here
}
1
Hardeep Singh

Une autre aide pour retarder votre code 100% d'utilisation rapide et permet éventuellement choisir un autre thread d'exécuter votre code retardé à partir de:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

Maintenant, vous simplement retardez votre code sur le thread principal} comme ceci:

delay(bySeconds: 1.5) { 
    // delayed code
}

Si vous voulez retarder votre code sur un autre thread} _:

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

Si vous préférez un Framework qui possède également des fonctionnalités plus pratiques, alors vérifiez HandySwift. Vous pouvez l'ajouter à votre projet via Carthage puis l'utiliser exactement comme dans les exemples ci-dessus, par exemple:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}
1
Dschee

Cela a fonctionné pour moi.

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

Objectif c:

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});
1
Ankit garg
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...
});

La fonction dispatch_after(_:_:_:) prend trois paramètres:

un délai
une file d'attente
un bloc ou une fermeture

La fonction dispatch_after(_:_:_:) appelle le bloc ou la fermeture de la file d’attribution qui lui est transmis après un délai donné. Notez que le retard est créé à l’aide de la fonction dispatch_time(_:_:). Rappelez-vous ceci car nous utilisons également cette fonction dans Swift.

Je recommande de suivre le didacticiel Raywenderlich Dispatch tutorial

1
CrazyPro007

Swift 4 a un moyen assez court de le faire:

Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
    // Your stuff here
    print("hello")
}
0
Vlady Veselinov

Voici la version synchrone de asyncAfter dans Swift:

let deadline = DispatchTime.now() + .seconds(3)
let semaphore = DispatchSemaphore.init(value: 0)
DispatchQueue.global().asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
    semaphore.signal()
}

semaphore.wait()

Avec un asynchrone:

let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}
0
Maxim Makhun

Maintenant, plus que du sucre syntaxique pour les envois asynchrones dans Grand Central Dispatch (GCD) dans Swift.

ajouter Podfile

pod 'AsyncSwift'

Ensuite, vous pouvez l'utiliser comme ça.

let seconds = 3.0
Async.main(after: seconds) {
print("Is called after 3 seconds")
}.background(after: 6.0) {
print("At least 3.0 seconds after previous block, and 6.0 after Async code is called")
}
0
Steven

utilisez ce code pour effectuer certaines tâches liées à l'interface utilisateur après 2,0 secondes.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })

Version Swift 3.0

La fonction de fermeture suivante exécute une tâche après le délai sur le thread principal. 

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Appelez cette fonction comme suit:

performAfterDelay(delay: 4.0) {
  print("test")
}
0
Himanshu Mahajan

Dans Swift 4

Utilisez cet extrait:

    let delayInSec = 1.0
    DispatchQueue.main.asyncAfter(deadline: .now() + delayInSec) {
       // code here
       print("It works")
    }
0
BlackRock