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?

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

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

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.


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

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
}

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

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
}

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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.