No Swift, como chamar o método com parâmetros no thread principal do GCD?

192

No meu aplicativo, tenho uma função que cria uma NSRURLSession e envia uma NSURLRequest usando

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

No bloco de conclusão desta tarefa, preciso fazer alguns cálculos que adicionem uma UIImage ao viewcontroller de chamada. Eu tenho uma função chamada

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

que faz o cálculo de adição de UIImage. Se eu tentar executar o código de adição de exibição dentro do bloco de conclusão, o Xcode emitirá um erro dizendo que não posso usar o mecanismo de layout enquanto estiver em um processo em segundo plano. Então, eu encontrei algum código no SO que tenta enfileirar um método no thread 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)
})

No entanto, não sei como adicionar os parâmetros "receiveAddr" e "amountBTC" a essa chamada de função. Como eu faria isso, ou alguém pode sugerir uma maneira ideal de adicionar uma chamada de método à fila principal do aplicativo?

amêndoa
fonte

Respostas:

496

As versões modernas do Swift usam DispatchQueue.main.asyncpara despachar para o encadeamento principal:

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

Para despachar depois na fila principal, use:

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

Versões anteriores do Swift usadas:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})
codester
fonte
Enquanto você está certo de que sua sugestão funciona, acho que minha resposta é um pouco melhor porque ela não chama UIApplication.sharedApplication, o que é incomum e pode afastar outros leitores do meu código. O escopo da minha resposta é restrito aos objetos de importância, enquanto o seu traz objetos auxiliares que exigem que eu leia mais documentos para aprender exatamente o que estou fazendo. E editei minha pergunta original para conter a chamada de função correta. Eu pensei que o displayQRCode não era específico o suficiente, mas com nossos comentários agora é. Obrigado por apontar isso.
almel
84

Versão Swift 3+ e Swift 4:

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

Swift 3 e Xcode 9.2:

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

Swift 2

Usando Trailing Closures, isso se torna:

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

Trailing Closures é o açúcar sintático Swift que permite definir o fechamento fora do escopo do parâmetro da função. Para obter mais informações, consulte Trailing Closures no Swift 2.2 Programming Language Guide.

No caso dispatch_async, a API é func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)uma vez que dispatch_block_té do tipo alias para () -> Void- Um fechamento que recebe 0 parâmetros e não possui um valor de retorno, e bloco sendo o último parâmetro da função, podemos definir o fechamento no escopo externo de dispatch_async.

Maxim Veksler
fonte
1
que era exatamente as 3 linhas Eu estou procurando ... agora você pode parar de ler minha mente
Laszlo
8

Recarregar coleçãoView on Main Thread

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

Aqui está a melhor sintaxe do estilo Swifty / Cocoa (IMO) para obter o mesmo resultado que as outras respostas:

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

Ou você pode pegar a popular biblioteca Async Swift para obter ainda menos código e mais funcionalidades:

Async.main {
    // Your code here
}
pejalo
fonte
método renomeado paraOperationQueue.main.addOperation({ }
Frostmourne 16/04
3

A maneira correta de fazer isso é usar dispatch_async no main_queue, como fiz no código a seguir

dispatch_async(dispatch_get_main_queue(), {
    (self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})
amêndoa
fonte
2

Aqui está uma pequena função global que você pode adicionar para obter uma sintaxe melhor:

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

E uso

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

Não se esqueça de enfraquecer a si mesmo se você estiver usando a si mesma dentro do fechamento.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})
villy393
fonte
1
Poderia explicar por que deveríamos estar fazendo isso?
Jackspicer
É porque ele pode criar ciclos de memória - ou seja, eu tenho uma forte referência a algo e tem uma forte referência a mim. Significando que nenhum de nós pode deixar a pilha de memória.
jackofallcode