Wie rufe ich in Swift eine Methode mit Parametern im GCD-Hauptthread auf?

192

In meiner App habe ich eine Funktion, die eine NSRURLSession erstellt und eine NSURLRequest mit sendet

sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error)

Im Abschlussblock für diese Aufgabe muss eine Berechnung durchgeführt werden, die dem aufrufenden Ansichtscontroller ein UIImage hinzufügt. Ich habe eine Funktion namens

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

das macht die UIImage-Addition Berechnung. Wenn ich versuche, den Code zum Hinzufügen von Ansichten innerhalb des Abschlussblocks auszuführen, gibt Xcode einen Fehler aus, der besagt, dass ich die Layout-Engine während eines Hintergrundprozesses nicht verwenden kann. Also habe ich auf SO Code gefunden, der versucht, eine Methode im Hauptthread in die Warteschlange zu stellen:

let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))

dispatch_after(time, dispatch_get_main_queue(), {
    let returned = UIApplication.sharedApplication().sendAction("displayQRCode:", to: self.delegate, from: self, forEvent: nil)
})

Ich weiß jedoch nicht, wie ich diesem Funktionsaufruf die Parameter "receiveAddr" und "amountBTC" hinzufügen soll. Wie würde ich das tun oder kann jemand einen optimalen Weg vorschlagen, um einen Methodenaufruf zur Hauptwarteschlange der Anwendung hinzuzufügen?

almel
quelle

Antworten:

496

Moderne Versionen von Swift werden DispatchQueue.main.asynczum Versenden an den Haupt-Thread verwendet:

DispatchQueue.main.async { 
  // your code here
}

Verwenden Sie zum Versenden in der Hauptwarteschlange Folgendes:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  // your code here
}

Ältere Versionen von Swift verwendet:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})
Codester
quelle
Obwohl Sie richtig sind, dass Ihr Vorschlag funktioniert, denke ich, dass meine Antwort etwas besser ist, da UIApplication.sharedApplication nicht aufgerufen wird, was ungewöhnlich ist und andere Leser meines Codes möglicherweise abschreckt. Der Umfang meiner Antwort ist auf die wichtigen Objekte beschränkt, während Ihre zusätzliche Objekte einbringt, bei denen ich mehr Dokumente lesen muss, um genau zu erfahren, was ich tue. Und ich habe meine ursprüngliche Frage so bearbeitet, dass sie den richtigen Funktionsaufruf enthält. Ich hatte gedacht, der displayQRCode sei nicht spezifisch genug, aber mit unseren Kommentaren ist es jetzt so. Vielen Dank für den Hinweis.
Almel
84

Swift 3+ & Swift 4 Version:

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

Swift 3 und Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}
DazChong
quelle
15

Swift 2

Bei Verwendung von Trailing Closures wird dies zu:

dispatch_async(dispatch_get_main_queue()) {
    self.tableView.reloadData()
}

Trailing Closures ist ein schneller syntaktischer Zucker, mit dem der Abschluss außerhalb des Funktionsparameterbereichs definiert werden kann. Weitere Informationen finden Sie unter Trailing Closures im Swift 2.2 Programmiersprachenhandbuch.

Im Fall dispatch_async ist die API func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)seit dem dispatch_block_tTyp Alias ​​für () -> Void- Ein Abschluss, der 0 Parameter empfängt und keinen Rückgabewert hat, und Block ist der letzte Parameter der Funktion, für die wir den Abschluss im äußeren Bereich von definieren können dispatch_async.

Maxim Veksler
quelle
1
das waren genau die 3 Zeilen, nach denen ich suche ... jetzt können Sie aufhören, meine Gedanken zu lesen
Laszlo
8

Laden Sie collectionView im Hauptthread neu

DispatchQueue.main.async {
    self.collectionView.reloadData()
}
Sanjay Mali
quelle
7

Hier ist die schönere (IMO) Swifty / Cocoa-Syntax, um das gleiche Ergebnis wie bei den anderen Antworten zu erzielen:

NSOperationQueue.mainQueue().addOperationWithBlock({
    // Your code here
})

Oder Sie können die beliebte Async Swift-Bibliothek für noch weniger Code und mehr Funktionalität nutzen:

Async.main {
    // Your code here
}
Pejalo
quelle
Methode umbenannt inOperationQueue.main.addOperation({ }
Frostmourne
3

Der richtige Weg, dies zu tun, besteht darin, dispatch_async in der main_queue zu verwenden, wie ich es im folgenden Code getan habe

dispatch_async(dispatch_get_main_queue(), {
    (self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})
almel
quelle
2

Hier ist eine nette kleine globale Funktion, die Sie für eine schönere Syntax hinzufügen können:

func dispatch_on_main(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Und Nutzung

dispatch_on_main {
    // Do some UI stuff
}
Mateusz Grzegorzek
quelle
2
//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Call your function here
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})
iOS
quelle
2

Vergiss nicht, dich selbst zu schwächen, wenn du dich innerhalb des Verschlusses verwendest.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})
villy393
quelle
1
Könnten Sie bitte erklären, warum wir das tun sollten?
Jackspicer
Es ist, weil es Gedächtniszyklen erzeugen kann - dh ich habe einen starken Bezug zu etwas und es hat einen starken Bezug zu mir. Das heißt, keiner von uns kann den Speicherhaufen verlassen.
Jackofallcode