En Swift, ¿cómo llamar al método con parámetros en el hilo principal de GCD?

192

En mi aplicación, tengo una función que hace una NSRURLSession y envía una NSURLRequest usando

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

En el bloque de finalización para esta tarea, necesito hacer algunos cálculos que agreguen un UIImage al controlador de vista de llamada. Tengo un func llamado

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

eso hace el cálculo de agregar UIImage. Si intento ejecutar el código de adición de vista dentro del bloque de finalización, Xcode arroja un error que dice que no puedo usar el motor de diseño mientras estoy en un proceso en segundo plano. Entonces encontré un código en SO que intenta poner en cola un método en el hilo principal:

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

Sin embargo, no sé cómo agregar los parámetros "recibirAddr" y "cantidadBTC" a esta llamada de función. ¿Cómo haría esto, o alguien puede sugerir una forma óptima de agregar una llamada de método a la cola principal de la aplicación?

almel
fuente

Respuestas:

496

Las versiones modernas de Swift usan DispatchQueue.main.asyncpara enviar al hilo principal:

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

Para despachar después en la cola principal, use:

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

Se utilizaron versiones anteriores de Swift:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})
codificador
fuente
Si bien tiene razón en que su sugerencia funciona, creo que mi respuesta es un poco mejor porque no hace una llamada a UIApplication.sharedApplication, lo cual es inusual y puede afectar a otros lectores de mi código. El alcance de mi respuesta se limita a los objetos de importancia, mientras que el suyo trae objetos auxiliares que requieren que lea más documentos para saber exactamente lo que estoy haciendo. Y he editado mi pregunta original para contener la llamada a la función correcta. Pensé que el displayQRCode no era lo suficientemente específico, pero con nuestros comentarios ahora lo es. Gracias por señalar eso.
almel
84

Versión Swift 3+ y Swift 4:

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

Swift 3 y Xcode 9.2:

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

Swift 2

El uso de cierres finales se convierte en:

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

Trailing Closures es un azúcar sintáctico Swift que permite definir el cierre fuera del alcance del parámetro de la función. Para obtener más información, consulte Cierres finales en la Guía del lenguaje de programación Swift 2.2.

En el caso de dispatch_async, la API es func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)ya que dispatch_block_tes un alias de tipo para () -> Void: un cierre que recibe 0 parámetros y no tiene un valor de retorno, y el bloque es el último parámetro de la función de la que podemos definir el cierre en el ámbito externo dispatch_async.

Maxim Veksler
fuente
1
esas fueron exactamente las 3 líneas que estoy buscando ... ahora puedes dejar de leer mi mente
Laszlo
8

Recargar colecciónVer en hilo principal

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

Aquí está la sintaxis de estilo Swifty / Cocoa más agradable (IMO) para lograr el mismo resultado que las otras respuestas:

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

O puede tomar la popular biblioteca Async Swift para obtener aún menos código y más funcionalidad:

Async.main {
    // Your code here
}
pejalo
fuente
método renombrado aOperationQueue.main.addOperation({ }
Frostmourne
3

La forma correcta de hacer esto es usar dispatch_async en main_queue, como hice en el siguiente código

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

Aquí hay una pequeña función global que puedes agregar para una sintaxis más agradable:

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

Y el uso

dispatch_on_main {
    // Do some UI stuff
}
Mateusz Grzegorzek
fuente
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
fuente
2

No olvide debilitarse si lo está usando dentro del cierre.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})
villy393
fuente
1
¿Podría explicar por qué deberíamos estar haciendo esto?
Jackspicer
Es porque puede crear ciclos de memoria, es decir, tengo una fuerte referencia a algo y tiene una fuerte referencia a mí. Lo que significa que ninguno de nosotros puede dejar el montón de memoria.
jackofallcode