Come posso dispatch_sync, dispatch_async, dispatch_after, ecc. In Swift 3, Swift 4 e oltre?


243

Ho un sacco di codice nei progetti Swift 2.x (o anche 1.x) che assomiglia a questo:

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

O cose del genere per ritardare l'esecuzione:

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

O qualsiasi altro tipo di utilizzo dell'API Grand Central Dispatch ...

Ora che ho aperto il mio progetto in Xcode 8 (beta) per Swift 3, ricevo tutti i tipi di errori. Alcuni offrono di correggere il mio codice, ma non tutte le correzioni producono codice funzionante. Cosa faccio al riguardo?


Risposte:


343

Sin dall'inizio, Swift ha fornito alcune strutture per rendere ObjC e C più Swifty, aggiungendo di più ad ogni versione. Ora, in Swift 3, la nuova funzione "importa come membro" consente ai framework con determinati stili di API C - in cui hai un tipo di dati che funziona in qualche modo come una classe e un sacco di funzioni globali con cui lavorare - agire più come API native di Swift. I tipi di dati vengono importati come classi Swift, le loro funzioni globali correlate vengono importate come metodi e proprietà su tali classi e alcune cose correlate come insiemi di costanti possono diventare sottotipi ove appropriato.

In Xcode 8 / Swift 3 beta, Apple ha applicato questa funzione (insieme ad alcune altre) per rendere il framework Dispatch molto più Swifty. (E anche Core Graphics .) Se hai seguito gli sforzi open source di Swift, questa non è una novità , ma ora è la prima volta che fa parte di Xcode.

Il primo passo per spostare qualsiasi progetto su Swift 3 dovrebbe essere quello di aprirlo in Xcode 8 e scegliere Modifica> Converti> In sintassi Swift corrente ... nel menu. Ciò applicherà (con la tua recensione e approvazione) tutte le modifiche necessarie per tutte le API rinominate e altre modifiche. (Spesso, una riga di codice è influenzata da più di una di queste modifiche contemporaneamente, quindi rispondendo alla correzione di errori, è possibile che individualmente non gestisca tutto correttamente.)

Il risultato è che il modello comune per far rimbalzare il lavoro sullo sfondo e viceversa ora appare così:

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

Nota che stiamo usando al .userInitiatedposto di una delle vecchie DISPATCH_QUEUE_PRIORITYcostanti. Gli identificatori di qualità del servizio (QoS) sono stati introdotti in OS X 10.10 / iOS 8.0, fornendo un modo più chiaro per il sistema di stabilire le priorità del lavoro e deprecare i vecchi identificatori di priorità. Consulta i documenti di Apple sul lavoro in background e l'efficienza energetica per i dettagli.

A proposito, se stai mantenendo le tue code per organizzare il lavoro, il modo per ottenerne uno ora appare così (nota che DispatchQueueAttributesè un OptionSet, quindi usi letterali in stile raccolta per combinare le opzioni):

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

Stai usando dispatch_afterper lavorare più tardi? Questo è anche un metodo per le code e richiede un DispatchTime, che ha operatori per vari tipi numerici in modo da poter aggiungere solo secondi interi o frazionari:

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

Puoi orientarti nella nuova API di Dispatch aprendo la sua interfaccia in Xcode 8 - usa Open Quickly per trovare il modulo Dispatch, oppure metti un simbolo (come DispatchQueue) nel tuo progetto / parco giochi Swift e fai clic su di esso, quindi fai un giro il modulo da lì. (Puoi trovare l' API Swift Dispatch nel nuovo sito Web di riferimento API di Apple e nel visualizzatore di documenti in-Xcode, ma sembra che il contenuto del documento della versione C non sia ancora stato spostato in esso.)

Consulta la Guida alla migrazione per ulteriori suggerimenti.


3
Per quanto riguarda Xcode 8 Beta 6, l'attributo .serial è sparito e il comportamento predefinito - forum.developer.apple.com/message/159457#159457
hyouuu,

6
Questo richiede un aggiornamento da XCode 8.1 .. l'etichetta degli attributi è scomparsa e al suo posto possiamo usare 'DispatchQueue.global (qos: .background) .async'
Mike M

2
Risposta meravigliosa Mi ha davvero aiutato a pensarci bene.
Mohsin Khubaib Ahmed il

Ho dovuto usare qos:invece diattributes:
Islam Q. il

Non dovrebbe essere che myQueue.async {nel class Fooesempio?
vacawama,

142

In Xcode 8 beta 4 non funziona ...

Uso:

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

per asincrono in due modi:

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

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

Quindi non blocca l'interfaccia utente?
user25

72

Questo è un buon esempio di Swift 4circa async:

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

ciao DispatchQueue.main.async {// Esegui aggiornamenti UI} è in esecuzione prima del thread in background
Uma Achanta

simile alle coroutine di Kotlin
user25

40

in Xcode 8 usa:

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

26

Swift 5.2, 4 e versioni successive

Code principali e di sfondo

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

Lavorare con i thread asincroni e sincronizzati !

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

I thread asincroni funzioneranno insieme al thread principale.

Sincronizza i thread bloccherà il thread principale durante l'esecuzione.


1
E come useresti i thread di sincronizzazione senza bloccare il thread principale (UI) ?? Vorrei eseguire una fila di cose in background - ma queste cose devono essere eseguite una dopo l'altra in modo sincronizzato. Durante questo periodo l'interfaccia utente dovrebbe rimanere reattiva .... Come lo faresti?
iKK,

Usa NSOperationQueue. Quale ogni tua attività rappresenta un NSOperation. fare riferimento stackoverflow.com/a/19746890/5215474
Saranjith

12

Swift 4.1 e 5. Utilizziamo le code in molti punti del nostro codice. Quindi, ho creato la classe Threads con tutte le code. Se non si desidera utilizzare la classe Threads, è possibile copiare il codice di coda desiderato dai metodi di 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()
    }
  }
}

Esempio che mostra l'uso della coda principale.

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.