¿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?

Brandonscript
fuente
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).

Robar
fuente
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.

Sverrisson
fuente
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!")
}
rockdaswift
fuente
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()
  }
}
Marco Pappalardo
fuente
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)")
 }
MD Ibrahim Hassan
fuente
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")
}
Suhit Patil
fuente
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ó

jjatie
fuente
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
}
iOS
fuente
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                 
})
Midhun p
fuente
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) {
    // ...
}
MANN
fuente
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)")
}
Ankit Garg
fuente
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
    }
Parth Dhorda
fuente
0

prueba esto

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
fuente
¿No está seguro de cómo esto es diferente de la respuesta afectada?
brandonscript