Wie kann ich dispatch_sync, dispatch_async, dispatch_after usw. in Swift 3, Swift 4 und darüber hinaus ausführen?

243

Ich habe viel Code in Swift 2.x (oder sogar 1.x) Projekten, der so aussieht:

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

Oder solche Sachen, um die Ausführung zu verzögern:

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

Oder jede andere Art von Verwendung der Grand Central Dispatch API ...

Nachdem ich mein Projekt in Xcode 8 (Beta) für Swift 3 geöffnet habe, werden alle möglichen Fehler angezeigt. Einige von ihnen bieten an, meinen Code zu reparieren, aber nicht alle Korrekturen erzeugen funktionierenden Code. Was mache ich dagegen?

Rickster
quelle

Antworten:

343

Von Anfang an hat Swift einige Funktionen bereitgestellt, um ObjC und C schneller zu machen, und mit jeder Version mehr hinzugefügt. In Swift 3 ermöglicht die neue Funktion "Als Mitglied importieren" Frameworks mit bestimmten Stilen der C-API - wobei Sie einen Datentyp haben, der wie eine Klasse funktioniert, und eine Reihe globaler Funktionen, um damit zu arbeiten -. verhalten sich eher wie Swift-native APIs. Die Datentypen werden als Swift-Klassen importiert, die zugehörigen globalen Funktionen werden als Methoden und Eigenschaften für diese Klassen importiert, und einige verwandte Dinge wie Konstantensätze können gegebenenfalls zu Untertypen werden.

In der Beta-Version von Xcode 8 / Swift 3 hat Apple diese Funktion (zusammen mit einigen anderen) angewendet, um das Dispatch-Framework wesentlich schneller zu gestalten. (Und auch Core Graphics .) Wenn Sie die Open-Source-Bemühungen von Swift verfolgt haben, ist dies keine Neuigkeit , aber jetzt ist es das erste Mal, dass es Teil von Xcode ist.

Der erste Schritt zum Verschieben eines Projekts nach Swift 3 sollte darin bestehen, es in Xcode 8 zu öffnen und im Menü Bearbeiten> Konvertieren> In aktuelle Swift-Syntax ... zu wählen . Dies gilt (mit Ihrer Überprüfung und Genehmigung) für alle Änderungen auf einmal, die für alle umbenannten APIs und andere Änderungen erforderlich sind. (Oft ist eine Codezeile von mehr als einer dieser Änderungen gleichzeitig betroffen, sodass eine individuelle Reaktion auf Fehlerbehebungen möglicherweise nicht alles richtig macht.)

Das Ergebnis ist, dass das übliche Muster für das Abprallen von Arbeit in den Hintergrund und zurück jetzt folgendermaßen aussieht:

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

Beachten Sie, dass wir .userInitiatedanstelle einer der alten DISPATCH_QUEUE_PRIORITYKonstanten verwenden. In OS X 10.10 / iOS 8.0 wurden QoS-Spezifizierer (Quality of Service) eingeführt, die dem System eine klarere Möglichkeit bieten, die Arbeit zu priorisieren und die alten Prioritätsspezifizierer zu verwerfen. Weitere Informationen finden Sie in den Apple- Dokumenten zu Hintergrundarbeiten und Energieeffizienz .

Übrigens, wenn Sie Ihre eigenen Warteschlangen behalten, um die Arbeit zu organisieren, sieht die Art und Weise, wie Sie eine erhalten, jetzt so aus (beachten Sie, dass dies DispatchQueueAttributeseine ist OptionSet, also verwenden Sie Literale im Sammlungsstil, um Optionen zu kombinieren):

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

Mit dispatch_afterspäterer Arbeit zu tun? Dies ist auch eine Methode für Warteschlangen, und es ist eine Methode erforderlich, DispatchTimedie Operatoren für verschiedene numerische Typen enthält, sodass Sie nur ganze oder gebrochene Sekunden hinzufügen können:

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

Sie können sich in der neuen Versand-API zurechtfinden, indem Sie die Benutzeroberfläche in Xcode 8 öffnen. Verwenden Sie Schnell öffnen, um das Versandmodul zu finden, oder fügen Sie ein Symbol (wie DispatchQueue) in Ihr Swift-Projekt / Ihren Spielplatz ein, klicken Sie bei gedrückter Befehlstaste darauf und stöbern Sie dann herum das Modul von dort. (Sie finden die Swift Dispatch- API auf Apples schicker neuer API-Referenzwebsite und im In-Xcode-Dokument-Viewer, aber es sieht so aus, als ob der Dokumentinhalt aus der C-Version noch nicht darin enthalten ist.)

Weitere Tipps finden Sie im Migrationshandbuch .

Rickster
quelle
3
Wie bei Xcode 8 Beta 6 ist das .serial-Attribut weg und das Standardverhalten - forums.developer.apple.com/message/159457#159457
hyouuu
6
Dies muss aktualisiert werden, da XCode 8.1 .. das Attribut-Label verschwunden ist und stattdessen 'DispatchQueue.global (qos: .background) .async' verwendet werden kann
Mike M
2
Wunderbare Antwort. Hat mir wirklich geholfen, mich darum zu kümmern.
Mohsin Khubaib Ahmed
Ich musste qos:anstelle vonattributes:
Islam Q.
Sollte das nicht myQueue.async {im class FooBeispiel sein?
Vacawama
142

In Xcode 8 funktioniert Beta 4 nicht ...

Verwenden:

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

für asynchrone zwei Möglichkeiten:

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

DispatchQueue.main.async( execute: {
    print("Async2")
})
ingconti
quelle
Die Benutzeroberfläche wird also nicht blockiert?
user25
72

Dieser ist ein gutes Beispiel für Swift 4ungefähr async:

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}
S. Matsepura
quelle
hi DispatchQueue.main.async {// UI-Updates ausführen} wird vor dem Hintergrund-Thread ausgeführt
Uma Achanta
ähnlich mit
Kotlins
40

in Xcode 8 verwenden:

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

Swift 5.2, 4 und höher

Haupt- und Hintergrundwarteschlangen

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

Arbeiten mit asynchronen und synchronisierten Threads!

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

Asynchrone Threads arbeiten mit dem Hauptthread zusammen.

Synchronisierungsthreads blockieren den Hauptthread während der Ausführung.

Saranjith
quelle
1
Und wie würden Sie Sync-Threads verwenden, ohne den Haupt-Thread (UI) zu blockieren? Ich möchte eine Reihe von Dingen im Hintergrund ausführen - aber diese Dinge müssen nacheinander synchron ausgeführt werden. Während dieser Zeit sollte die Benutzeroberfläche reagieren ... Wie würden Sie das tun?
iKK
Verwenden Sie NSOperationQueue. Welche jeder Ihrer Aufgaben eine NSOperation darstellt. siehe stackoverflow.com/a/19746890/5215474
Saranjith
12

Swift 4.1 und 5. Wir verwenden Warteschlangen an vielen Stellen in unserem Code. Also habe ich die Threads-Klasse mit allen Warteschlangen erstellt. Wenn Sie die Threads-Klasse nicht verwenden möchten, können Sie den gewünschten Warteschlangencode aus den Klassenmethoden kopieren.

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

Beispiel für die Verwendung der Hauptwarteschlange.

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
Gurjinder Singh
quelle