¿Cómo escribo dispatch_after GCD en Swift 3, 4 y 5?


445

En Swift 2, pude usar dispatch_afterpara retrasar una acción usando el despacho central magnífico:

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

Pero esto ya no parece compilarse desde Swift 3. ¿Cuál es la forma preferida de escribir esto en Swift moderno?


66
Puede encontrar más información sobre el proceso de migración aquí: https://swift.org/migration-guide/ La sección "Despacho" es relevante para esta pregunta
tonik12

debería ser tu pregunta UInt64?
Miel

Respuestas:


1125

La sintaxis es simplemente:

// to run something in 0.1 seconds

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

Tenga en cuenta que la sintaxis anterior de agregar secondscomo a Doubleparece ser una fuente de confusión (especialmente porque estábamos acostumbrados a agregar nsec). Esa Doublesintaxis de "agregar segundos como " funciona porque deadlinees una DispatchTimey, detrás de escena, hay un +operador que tomará una Doubley agregará esa cantidad de segundos a DispatchTime:

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

Pero, si realmente desea agregar un número entero de mseg, μs o nsec a DispatchTime, también puede agregar a DispatchTimeIntervala a DispatchTime. Eso significa que puedes hacer:

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

Todo esto funciona a la perfección debido a este método de sobrecarga separado para el +operador de la DispatchTimeclase.

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

Se le preguntó cómo se cancela una tarea despachada. Para hacer esto, use DispatchWorkItem. Por ejemplo, esto inicia una tarea que se activará en cinco segundos, o si el controlador de vista se descarta y se desasigna, deinitcancelará la tarea:

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

}

Tenga en cuenta el uso de la [weak self]lista de captura en el DispatchWorkItem. Esto es esencial para evitar un ciclo de referencia fuerte. También tenga en cuenta que esto no hace una cancelación preventiva, sino que simplemente detiene la tarea de comenzar si aún no lo ha hecho. Pero si ya ha comenzado cuando encuentra la cancel()llamada, el bloque finalizará su ejecución (a menos que esté verificando manualmente isCancelleddentro del bloque).


55
Gracias por señalarlo, y de hecho swift.org/migration-guide menciona la necesidad de hacer ese cambio a mano.
mate

1
Oh, lo siento. Es muy tarde aquí :). Pensé que todo el desorden debería desaparecer, pero no dio el salto. En mi opinión, la solución "simple" es la única solución verdadera.
tobiasdm

1
@ Rob ¿cómo haría para cancelarlo? Gracias.
kemicofa fantasma

Ok, entonces, ¿cómo se agrega una espera dinámica? Por ejemplo, tengo un número let: Float = 1.0. Y .now () + .milliseconds (número) no funciona. Tampoco Double (número). No puedo resolverlo.
Kjell el

2
Las DispatchTimeIntervalentregas, como .millisecondsrequieren Int. Pero si solo agregara segundos, usaría Double, por ejemplo let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob

128

Swift 4:

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

Por el momento .seconds(Int), .microseconds(Int)y .nanoseconds(Int)también puede ser utilizado.


77
.millisecondsEs mejor que el doble.
DawnSong

55
Muy agradable. Una nota para otros: también puede usar cualquiera de los otros DispatchTimeIntervalvalores de enumeración. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern

@RobMacEachern, gracias, esa es una buena sugerencia. La agrego a la respuesta.
Sverrisson

2
.milliseconds is better than Double. - Quiero eso en una camiseta;).
Chris Prince

58

Si solo quieres la función de retraso en

Swift 4 y 5

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

Puedes usarlo como:

delay(interval: 1) { 
    print("Hi!")
}

DispatchQueue.main.asyncAfter (fecha límite:) no funciona. Dice que no sobrecarga ningún método de su superclase.
Fabrizio Bartolomucci

77
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)es más simple
DawnSong

16

después del lanzamiento de Swift 3, también se debe agregar @escaping

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

5

Un sabor algo diferente de la respuesta aceptada.

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

5

Swift 4

Puede crear una extensión en DispatchQueue y agregar un retraso de función que use la DispatchQueuefunción asyncAfter internamente

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

y use

DispatchQueue.delay(.milliseconds(10)) {
   print("task to be done")
}

2
¿Cómo es esto diferente de la respuesta de @ rockdaswift?
brandonscript

como mencioné, envuelve asyncAfter dentro de la función performAfter que toma la demora como parámetro y puede ser más fácil llamar usando solo performAfter (delay: 2) {}
Suhit Patil

Los parámetros de cierre no escapan por defecto, @escaping indica que un parámetro de cierre puede escapar. Se agregó el parámetro @ escape en el cierre para evitar un posible bloqueo.
Suhit Patil

3

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

Recomiendo encarecidamente utilizar las herramientas de Xcode para convertir a Swift 3 (Edición> Convertir> A sintaxis Swift actual). Esto me atrapó


3

En Swift 4.1 y Xcode 9.4.1

La respuesta simple es ...

//To call function after 5 seconds time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}

3
¿No está seguro de cómo esto es diferente a la respuesta aceptada?
brandonscript

3

Swift 5 y superior

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

1

Ninguna de las respuestas mencionó ejecutarse en un hilo no principal, por lo que agregué mis 2 centavos.

En la cola principal (hilo principal)

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

O

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

En la cola global (subproceso no principal, basado en QOS especificado).

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

O

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

0

Esto funcionó para mí en 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)")
}

55
¿No está seguro de cómo difiere esto de la respuesta aceptada?
brandonscript

0

Puedes usar

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

0

prueba esto

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }

¿No está seguro de cómo esto es diferente de la respuesta afectada?
brandonscript
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.