web-dev-qa-db-fra.com

Comment puis-je dispatch_sync, dispatch_async, dispatch_after, etc. dans Swift 3, Swift 4 et au-delà?

J'ai beaucoup de code dans les projets Swift 2.x (ou même 1.x) qui ressemble à ceci:

// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = image
    }
}

Ou des choses comme ça pour retarder l'exécution:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
    print("test")
}

Ou tout autre type d'utilisation de l'API Grand Central Dispatch ...

Maintenant que j'ai ouvert mon projet dans Xcode 8 (version bêta) pour Swift 3, j'ai toutes sortes d'erreurs. Certains proposent de corriger mon code, mais tous les correctifs ne produisent pas de code fonctionnel. Qu'est-ce que je fais à ce sujet?

226
rickster

Depuis le début, Swift a fourni quelques facilités pour rendre ObjC et C plus Swifty, en ajoutant davantage avec chaque version. Désormais, dans Swift 3, la nouvelle fonctionnalité "importation en tant que membre" permet aux frameworks d'utiliser certains styles de l'API C - dans lesquels vous disposez d'un type de données qui fonctionne un peu comme une classe. et un ensemble de fonctions globales pour l’utiliser - agissent davantage comme des API natives Swift. Les types de données importés sous forme de classes Swift, leurs fonctions globales associées importées sous forme de méthodes et de propriétés sur ces classes, ainsi que certains éléments connexes tels que les ensembles de constantes peuvent devenir des sous-types, le cas échéant.

Dans Xcode 8/Swift 3 bêta, Apple a appliqué cette fonctionnalité (avec quelques autres) pour rendre le cadre Dispatch beaucoup plus dynamique. (Et Core Graphics , aussi.) Si vous avez suivi les Swift efforts open-source, ce n'est pas une nouvelle , mais c'est maintenant la première fois, cela fait partie de Xcode.

Votre première étape pour déplacer un projet sur Swift 3 devrait être de l'ouvrir dans Xcode 8 et de choisir Édition> Convertir> Vers courant Swift Syntaxe ... dans le menu. Cela s'appliquera (avec votre révision et votre approbation) à toutes les modifications nécessaires en même temps pour toutes les API renommées et autres modifications. (Il arrive souvent qu'une ligne de code soit affectée par plusieurs modifications en même temps. Il est donc possible que le fait de réagir à une correction d'erreur ne gère pas tout correctement.)

Il en résulte que le schéma habituel de renvoi du travail en arrière-plan se présente comme suit:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

Notez que nous utilisons .userInitiated au lieu de l'une des anciennes constantes DISPATCH_QUEUE_PRIORITY. Les spécificateurs de qualité de service (QoS) ont été introduits dans OS X 10.10/iOS 8.0, offrant au système un moyen plus clair de hiérarchiser les tâches et de désapprouver les anciens spécificateurs de priorité. Pour plus de détails, reportez-vous à la documentation d'Apple Documents sur les tâches de fond et l'efficacité énergétique .

À propos, si vous gardez vos propres files d'attente pour organiser le travail, la manière de l'obtenir ressemble à ceci (notez que DispatchQueueAttributes est un OptionSet, vous utilisez donc des littéraux de style collection pour combiner Les options):

class Foo { 
    let queue = DispatchQueue(label: "com.example.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello World")
        }
    }
}

Utiliser dispatch_after pour travailler plus tard? C'est aussi une méthode sur les files d'attente, et il faut un DispatchTime, qui possède des opérateurs pour différents types numériques, de sorte que vous pouvez simplement ajouter des secondes entières ou fractionnaires:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}

Vous pouvez vous repérer dans la nouvelle API Dispatch en ouvrant son interface dans Xcode 8 - utilisez Open Quickly pour trouver le module Dispatch ou insérez un symbole (comme DispatchQueue) dans votre projet Swift/playground et cliquez sur la commande, puis créez une liste autour du module à partir de là. (Vous pouvez trouver l'API Swift Dispatch dans le tout nouveau site Web de référence API d'Apple et dans la visionneuse de doc in-Xcode, mais le contenu de la doc de la version C n'a pas été déplacé. dans pour l'instant.)

Voir le Guide de migration pour plus de conseils.

326
rickster

Dans Xcode 8 beta 4 ne fonctionne pas ...

Utilisation:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("Are we there yet?")
}

pour async deux manières:

DispatchQueue.main.async {
    print("Async1")
}

DispatchQueue.main.async( execute: {
    print("Async2")
})
137
ingconti

Celui-ci est un bon exemple pour Swift 4 à propos de async:

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}
70
S. Matsepura

dans Xcode 8, utilisez:

DispatchQueue.global(qos: .userInitiated).async { }
38
Marco

Swift 4

Files d'attente principales et d'arrière-plan

let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread") 

Travailler avec les threads async et sync!

 background.async { //async tasks here } 
 background.sync { //sync tasks here } 

Les threads asynchrones fonctionneront avec le thread principal.

Les threads de synchronisation bloqueront le thread principal lors de l'exécution.

22
Saranjith

Swift 4.1 et 5. Nous utilisons des files d'attente dans de nombreux endroits de notre code. J'ai donc créé la classe Threads avec toutes les files d'attente. Si vous ne souhaitez pas utiliser la classe Threads, vous pouvez copier le code de file d'attente souhaité à partir des méthodes de classe.

class Threads {

  static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
  static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")

  // Main Queue
  class func performTaskInMainQueue(task: @escaping ()->()) {
    DispatchQueue.main.async {
      task()
    }
  }

  // Background Queue
  class func performTaskInBackground(task:@escaping () throws -> ()) {
    DispatchQueue.global(qos: .background).async {
      do {
        try task()
      } catch let error as NSError {
        print("error in background thread:\(error.localizedDescription)")
      }
    }
  }

  // Concurrent Queue
  class func perfromTaskInConcurrentQueue(task:@escaping () throws -> ()) {
    concurrentQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Concurrent Queue:\(error.localizedDescription)")
      }
    }
  }

  // Serial Queue
  class func perfromTaskInSerialQueue(task:@escaping () throws -> ()) {
    serialQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Serial Queue:\(error.localizedDescription)")
      }
    }
  }

  // Perform task afterDelay
  class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:@escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
      task()
    }
  }
}

Exemple illustrant l'utilisation de la file d'attente principale.

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
8
GSK