In Swift come chiamare il metodo con i parametri sul thread principale GCD?


192

Nella mia app ho una funzione che crea una NSRURLSession e invia una NSURLRequest utilizzando

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

Nel blocco di completamento per questa attività, devo eseguire alcuni calcoli che aggiungono un UIImage al viewcontroller chiamante. Ho una funzione chiamata

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

che esegue il calcolo dell'aggiunta di UIImage. Se provo a eseguire il codice di aggiunta della vista all'interno del blocco di completamento, Xcode genera un errore che dice che non posso usare il motore di layout durante un processo in background. Quindi ho trovato del codice su SO che tenta di mettere in coda un metodo sul thread principale:

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

Tuttavia, non so come aggiungere i parametri "receiveAddr" e "amountBTC" a questa chiamata di funzione. Come farei questo o qualcuno può suggerire un modo ottimale per aggiungere una chiamata di metodo alla coda principale dell'applicazione?

Risposte:


497

Le versioni moderne di Swift usano DispatchQueue.main.asyncper spedire al thread principale:

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

Per inviare dopo sulla coda principale, utilizzare:

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

Versioni precedenti di Swift utilizzate:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})

Mentre hai ragione sul fatto che il tuo suggerimento funzioni, penso che la mia risposta sia leggermente migliore perché non fa una chiamata a UIApplication.sharedApplication, che è insolito e potrebbe buttare via altri lettori del mio codice. Lo scopo della mia risposta è limitato agli oggetti di importanza, mentre il tuo introduce oggetti accessori che mi richiedono di leggere più documenti per imparare esattamente cosa sto facendo. E ho modificato la mia domanda originale per contenere la chiamata di funzione corretta. Pensavo che displayQRCode non fosse abbastanza specifico, ma con i nostri commenti ora lo è. Grazie per la segnalazione.
almel

84

Swift 3+ e Swift 4 versione:

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

Swift 3 e Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}

15

Swift 2

Usando le chiusure finali questo diventa:

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

Trailing Closures è lo zucchero sintattico Swift che consente di definire la chiusura al di fuori dell'ambito del parametro della funzione. Per ulteriori informazioni, consultare Chiusure finali nella Guida al linguaggio di programmazione Swift 2.2.

Nel caso dispatch_async l'API è in func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)quanto dispatch_block_ttipo alias per () -> Void- Una chiusura che riceve 0 parametri e non ha un valore di ritorno, e il blocco essendo l'ultimo parametro della funzione di cui possiamo definire la chiusura nell'ambito esterno dispatch_async.


1
erano esattamente le 3 righe che sto cercando ... ora puoi smettere di leggermi nella mente
Laszlo

8

Ricarica raccolta Visualizza sul thread principale

DispatchQueue.main.async {
    self.collectionView.reloadData()
}

7

Ecco la sintassi più bella (IMO) Swifty / Cocoa per ottenere lo stesso risultato delle altre risposte:

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

Oppure potresti prendere la popolare libreria Async Swift per ancora meno codice e più funzionalità:

Async.main {
    // Your code here
}

metodo rinominato inOperationQueue.main.addOperation({ }
Frostmourne il

3

Il modo corretto per farlo è usare dispatch_async nel main_queue, come ho fatto nel seguente codice

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

2

Ecco una piccola funzione globale che puoi aggiungere per una sintassi migliore:

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

E utilizzo

dispatch_on_main {
    // Do some UI stuff
}

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

2

Non dimenticare di indebolire se stessi se stai usando sé all'interno della chiusura.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})

1
Potresti spiegare perché dovremmo farlo?
Jackspicer,

È perché può creare cicli di memoria - cioè ho un forte riferimento a qualcosa e ha un forte riferimento a me. Significa che nessuno di noi può lasciare il mucchio di memoria.
codice jackofall
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.