Como escrevo GCD de dispatch_after no Swift 3, 4 e 5?

445

No Swift 2, pude dispatch_afteradiar uma ação usando o Grand Central Dispatch:

var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) 
dispatch_after(dispatchTime, dispatch_get_main_queue(), { 
    // your function here 
})

Mas isso não parece mais compilar desde o Swift 3. Qual é a maneira preferida de escrever isso no Swift moderno?

brandonscript
fonte
6
Outras informações sobre o processo de migração pode ser encontrada aqui: https://swift.org/migration-guide/ A seção "Despacho" é o relevante para esta pergunta
tonik12
sua pergunta deveria ser UInt64?
Mel

Respostas:

1125

A sintaxe é simplesmente:

// to run something in 0.1 seconds

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

Observe que a sintaxe acima de adicionar secondscomo a Doubleparece ser uma fonte de confusão (especialmente porque estávamos acostumados a adicionar o nsec). A Doublesintaxe "adicionar segundos como " funciona porque deadlineé um DispatchTimee, nos bastidores, há um +operador que fará um Doublee adicionará muitos segundos ao DispatchTime:

public func +(time: DispatchTime, seconds: Double) -> DispatchTime

Mas, se você realmente deseja adicionar um número inteiro de ms, ms ou nsec ao DispatchTime, também pode adicionar um DispatchTimeIntervala a DispatchTime. Isso significa que você pode fazer:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
    os_log("500 msec seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(1_000_000)) {
    os_log("1m μs seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .nanoseconds(1_500_000_000)) {
    os_log("1.5b nsec seconds later")
}

Tudo isso funciona perfeitamente por causa desse método de sobrecarga separado para o +operador da DispatchTimeclasse.

public func +(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime

Foi perguntado como alguém cancela uma tarefa despachada. Para fazer isso, use DispatchWorkItem. Por exemplo, isso inicia uma tarefa que será acionada em cinco segundos ou, se o controlador de exibição for descartado e desalocado, deinitcancelará a tarefa:

class ViewController: UIViewController {

    private var item: DispatchWorkItem?

    override func viewDidLoad() {
        super.viewDidLoad()

        item = DispatchWorkItem { [weak self] in
            self?.doSomething()
            self?.item = nil
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: item!)
    }

    deinit {
        item?.cancel()
    }

    func doSomething() { ... }

}

Observe o uso da [weak self]lista de captura no DispatchWorkItem. Isso é essencial para evitar um forte ciclo de referência. Observe também que isso não faz um cancelamento preventivo, mas apenas interrompe o início da tarefa, se ainda não o tiver feito. Mas se já tiver iniciado no momento em que encontrar a cancel()chamada, o bloco concluirá sua execução (a menos que você esteja verificando manualmente isCancelleddentro do bloco).

Roubar
fonte
5
Obrigado por apontar isso e, de fato, o swift.org/migration-guide menciona a necessidade de fazer essa alteração manualmente.
Matt
1
Oh, desculpe. É tarde demais aqui :). Pensei que toda a bagunça deveria ir realmente, mas não deu o salto. Na IMO, a solução "simples" é a única solução verdadeira.
tobiasdm
1
@ Rob como eu iria cancelá-lo? Obrigado.
Kemicofa ghost # 4/16
Ok, então, como você adiciona uma espera dinâmica? Por exemplo, eu tenho um número let: Float = 1.0. E .now () + .milliseconds (number) não funciona. Nem o dobro (número). Eu não consigo entender.
Kjell
2
As DispatchTimeIntervalexecuções, como .millisecondsexigem Int. Mas se apenas adicionando segundos, eu usaria Double, por exemplo let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob
128

Swift 4:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
   // Code
}

Por enquanto .seconds(Int), .microseconds(Int)e .nanoseconds(Int)também pode ser usado.

Sverrisson
fonte
7
.millisecondsé melhor que o dobro.
precisa saber é o seguinte
5
Muito agradável. Uma observação para outras pessoas: você também pode usar qualquer um dos outros DispatchTimeIntervalvalores de enumeração. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
precisa
@RobMacEachern, obrigado. É uma boa sugestão. Adicionei a resposta.
Sverrisson 28/10
2
.milliseconds is better than Double. - Quero isso em uma camiseta;).
Chris Prince
58

Se você quer apenas a função de atraso em

Swift 4 e 5

func delay(interval: TimeInterval, closure: @escaping () -> Void) {
     DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
          closure()
     }
}

Você pode usá-lo como:

delay(interval: 1) { 
    print("Hi!")
}
rockdaswift
fonte
DispatchQueue.main.asyncAfter (deadline:) não funciona. Ele diz que não sobrecarrega nenhum método de sua superclasse.
Fabrizio Bartolomucci
7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)é mais simples.
precisa saber é o seguinte
16

após o lançamento do Swift 3, também é necessário adicionar o @escaping

func delay(_ delay: Double, closure: @escaping () -> ()) {
  DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    closure()
  }
}
Marco Pappalardo
fonte
5

Um sabor um pouco diferente da Resposta Aceita.

Swift 4

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 + .milliseconds(500) + 
.microseconds(500) + .nanoseconds(1000)) {
                print("Delayed by 0.1 second + 500 milliseconds + 500 microseconds + 
                      1000 nanoseconds)")
 }
Mestre Ibrahim Hassan
fonte
5

Swift 4

Você pode criar uma extensão no DispatchQueue e adicionar atraso de função que usa a DispatchQueuefunção asyncAfter internamente

extension DispatchQueue {
   static func delay(_ delay: DispatchTimeInterval, closure: @escaping () -> ()) {
      DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: closure)
   }
}

E use

DispatchQueue.delay(.milliseconds(10)) {
   print("task to be done")
}
Suhit Patil
fonte
2
Como isso é diferente da resposta do @ rockdaswift?
brandonscript
como eu mencionei que envolve asyncAfter dentro função performAfter que leva atraso como parâmetro e pode ser mais fácil chamar usando apenas performAfter (atraso: 2) {}
Suhit Patil
Os parâmetros de fechamento são sem escape por padrão, @escaping indica que um parâmetro de fechamento pode escapar. adicionado parâmetro @ escape no fechamento para salvar uma possível falha.
Suhit Patil
3

ligar DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Eu recomendo usar as ferramentas do Xcode para converter para o Swift 3 (Editar> Converter> na sintaxe do Swift atual). Pegou isso para mim

jjatie
fonte
3

No Swift 4.1 e no Xcode 9.4.1

Resposta simples é ...

//To call function after 5 seconds time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}
iOS
fonte
3
Não sabe ao certo como isso é diferente da resposta aceita?
brandonscript
3

Swift 5 e acima

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})
midhun p
fonte
1

Nenhuma das respostas mencionou a execução em um segmento não principal, portanto, adicionei meus 2 centavos.

Na fila principal (encadeamento principal)

let mainQueue = DispatchQueue.main
let deadline = DispatchTime.now() + .seconds(10)
mainQueue.asyncAfter(deadline: deadline) {
    // ...
}

OU

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(10)) { 
    // ...
}

Na fila global (encadeamento não principal, com base no QOS especificado).

let backgroundQueue = DispatchQueue.global()
let deadline = DispatchTime.now() + .milliseconds(100)
backgroundQueue.asyncAfter(deadline: deadline, qos: .background) { 
    // ...
}

OU

DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + .milliseconds(100), qos: .background) {
    // ...
}
MANN
fonte
0

Isso funcionou para mim no Swift 3

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds


DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}
Ankit garg
fonte
5
Não sabe ao certo como isso difere da resposta aceita?
brandonscript
0

Você pode usar

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(100)) {
        // Code
    }
Parth Dhorda
fonte
0

tente isso

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
fonte
Não tem certeza de como isso é diferente da resposta afetada?
brandonscript