Revisé el iBook de Apple y no pude encontrar ninguna definición:
¿Alguien puede explicar la estructura de dispatch_after
?
dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)
Revisé el iBook de Apple y no pude encontrar ninguna definición:
¿Alguien puede explicar la estructura de dispatch_after
?
dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)
Respuestas:
Una idea más clara de la estructura:
dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)
dispatch_time_t
es un UInt64
. En dispatch_queue_t
realidad, el tipo está alias a an NSObject
, pero solo debe usar sus métodos GCD familiares para obtener colas. El bloque es un cierre Swift. Específicamente, dispatch_block_t
se define como () -> Void
, que es equivalente a () -> ()
.
Ejemplo de uso:
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
print("test")
}
EDITAR:
Recomiendo usar la delay
función realmente agradable de @ matt .
EDITAR 2:
En Swift 3, habrá nuevos contenedores para GCD. Ver aquí: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md
El ejemplo original se escribiría de la siguiente manera en Swift 3:
let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
print("test")
}
Tenga en cuenta que puede escribir la deadlineTime
declaración como DispatchTime.now() + 1.0
y obtener el mismo resultado porque el +
operador se anula de la siguiente manera (de manera similar para -
):
func +(time: DispatchTime, seconds: Double) -> DispatchTime
func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime
Esto significa que si no usa el DispatchTimeInterval
enum
y simplemente escribe un número, se supone que está usando segundos.
dispatch_after(1, dispatch_get_main_queue()) { println("test") }
1
de dispatch_after(1, ...
puede causar mucha confusión aquí. La gente pensará que es una cantidad de segundos, cuando en realidad es nano-segundo . Sugiero ver la respuesta de @brindy sobre cómo crear este número correctamente.
1
a dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
porque conduce a la confusión. La gente podría pensar que no es necesario crear un dispatch_time_t en Swift
Binary operator '+' cannot be applied to operands of type DispatchTime and '_'
en la línealet delayTime = DispatchTime.now() + .seconds(1.0)
DispatchTime.now() + 1.0
parece ser la única forma de hacerlo funcionar (no es necesario .seconds
)
Utilizo dispatch_after
tantas veces que escribí una función de utilidad de nivel superior para simplificar la sintaxis:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
Y ahora puedes hablar así:
delay(0.4) {
// do stuff
}
Wow, un idioma donde puedes mejorar el idioma. ¿Que podría ser mejor?
Parece que no vale la pena molestarse, ahora que han mejorado la sintaxis de llamadas:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }
return
).
1.0 ~~ { code...}
Swift 3+
Esto es súper fácil y elegante en Swift 3+:
DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
// ...
}
Respuesta anterior:
Para ampliar la respuesta de Cezary, que se ejecutará después de 1 nanosegundo, tuve que hacer lo siguiente para ejecutar después de 4 segundos y medio.
let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)
Editar: descubrí que mi código original estaba ligeramente equivocado. La escritura implícita provoca un error de compilación si no convierte NSEC_PER_SEC en un Double.
Si alguien puede sugerir una solución más óptima, me gustaría escucharla.
dispatch_get_current_queue()
. Yo solía en su dispatch_get_main_queue()
lugar.
dispatch_get_main_queue()
definitivamente es lo que deberías estar usando. Actualizará.
la sintaxis de matt es muy buena y si necesita invalidar el bloque, puede usar esto:
typealias dispatch_cancelable_closure = (cancel : Bool) -> Void
func delay(time:NSTimeInterval, closure:()->Void) -> dispatch_cancelable_closure? {
func dispatch_later(clsr:()->Void) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), clsr)
}
var closure:dispatch_block_t? = closure
var cancelableClosure:dispatch_cancelable_closure?
let delayedClosure:dispatch_cancelable_closure = { cancel in
if closure != nil {
if (cancel == false) {
dispatch_async(dispatch_get_main_queue(), closure!);
}
}
closure = nil
cancelableClosure = nil
}
cancelableClosure = delayedClosure
dispatch_later {
if let delayedClosure = cancelableClosure {
delayedClosure(cancel: false)
}
}
return cancelableClosure;
}
func cancel_delay(closure:dispatch_cancelable_closure?) {
if closure != nil {
closure!(cancel: true)
}
}
Use de la siguiente manera
let retVal = delay(2.0) {
println("Later")
}
delay(1.0) {
cancel_delay(retVal)
}
El enlace de arriba parece estar inactivo. Código Objc original de Github
performSelector:afterDelay:
ahora está disponible en Swift 2, por lo que puede cancelarlo.
dispatch_source_t
, porque eso es algo que puede cancelar).
Apple tiene un fragmento de dispatch_after para Objective-C :
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
<#code to be executed after a specified delay#>
});
Aquí está el mismo fragmento portado a Swift 3:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + <#delayInSeconds#>) {
<#code to be executed after a specified delay#>
}
Otra forma es extender Double así:
extension Double {
var dispatchTime: dispatch_time_t {
get {
return dispatch_time(DISPATCH_TIME_NOW,Int64(self * Double(NSEC_PER_SEC)))
}
}
}
Entonces puedes usarlo así:
dispatch_after(Double(2.0).dispatchTime, dispatch_get_main_queue(), { () -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
})
Me gusta la función de retraso de Matt, pero solo por preferencia, prefiero limitar los pases de cierre.
En Swift 3.0
Despacho de colas
DispatchQueue(label: "test").async {
//long running Background Task
for obj in 0...1000 {
print("async \(obj)")
}
// UI update in main queue
DispatchQueue.main.async(execute: {
print("UI update on main queue")
})
}
DispatchQueue(label: "m").sync {
//long running Background Task
for obj in 0...1000 {
print("sync \(obj)")
}
// UI update in main queue
DispatchQueue.main.sync(execute: {
print("UI update on main queue")
})
}
Despacho después de 5 segundos
DispatchQueue.main.after(when: DispatchTime.now() + 5) {
print("Dispatch after 5 sec")
}
Versión Swift 3.0
Después de la función de cierre, ejecute alguna tarea después del retraso en el hilo principal.
func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
onCompletion()
})
}
Llame a esta función como:
performAfterDelay(delay: 4.0) {
print("test")
}
1) Agregue este método como parte de la extensión UIViewController.
extension UIViewController{
func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue(), block)
}
}
Llame a este método en VC:
self.runAfterDelay(5.0, block: {
//Add code to this block
print("run After Delay Success")
})
2)
performSelector("yourMethod Name", withObject: nil, afterDelay: 1)
3)
override func viewWillAppear(animated: Bool) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue(), { () -> () in
//Code Here
})
// Forma compacta
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) {
//Code here
}
}
Aunque no es la pregunta original del OP, ciertas NSTimer
preguntas relacionadas se han marcado como duplicados de esta pregunta, por lo que vale la pena incluir una NSTimer
respuesta aquí.
NSTimer
vs dispatch_after
NSTimer
es más alto nivel mientras que dispatch_after
es más bajo nivel.NSTimer
Es más fácil de cancelar. Cancelar dispatch_after
requiere escribir más código .NSTimer
Crea una NSTimer
instancia.
var timer = NSTimer()
Inicie el temporizador con el retraso que necesita.
// invalidate the timer if there is any chance that it could have been called before
timer.invalidate()
// delay of 2 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
Agregue una función que se llamará después del retraso (usando cualquier nombre que haya utilizado para el selector
parámetro anterior).
func delayedAction() {
print("Delayed action has now started."
}
timer.invalidate()
.repeats: true
.Si tiene un evento único sin necesidad de cancelar, no es necesario crear la timer
variable de instancia. Lo siguiente será suficiente:
NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
Vea mi respuesta más completa aquí .
Para múltiples funciones use esto. Es muy útil usar animaciones o el cargador de actividades para funciones estáticas o cualquier actualización de la interfaz de usuario.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
// Call your function 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// Call your function 2
}
}
Por ejemplo: use una animación antes de que se recargue una tableView. O cualquier otra actualización de la interfaz de usuario después de la animación.
*// Start your amination*
self.startAnimation()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
*// The animation will execute depending on the delay time*
self.stopAnimation()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
*// Now update your view*
self.fetchData()
self.updateUI()
}
}
Esto funcionó para mí.
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)")
}
C objetivo:
CGFloat time1 = 3.49;
CGFloat time2 = 8.13;
// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CGFloat newTime = time1 + time2;
NSLog(@"New time: %f", newTime);
});
Swift 3 y 4:
Puede crear una extensión en DispatchQueue y agregar un retraso de función que utilice la función DispatchQueue asyncAfter internamente
extension DispatchQueue {
static func delay(_ delay: DispatchTimeInterval, closure: @escaping () -> ()) {
let timeInterval = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: timeInterval, execute: closure)
}
}
utilizar:
DispatchQueue.delay(.seconds(1)) {
print("This is after delay")
}
Otro ayudante para retrasar su código que es 100% rápido en el uso y opcionalmente permite elegir un hilo diferente para ejecutar su código retrasado desde:
public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
let dispatchTime = DispatchTime.now() + seconds
dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}
public enum DispatchLevel {
case main, userInteractive, userInitiated, utility, background
var dispatchQueue: DispatchQueue {
switch self {
case .main: return DispatchQueue.main
case .userInteractive: return DispatchQueue.global(qos: .userInteractive)
case .userInitiated: return DispatchQueue.global(qos: .userInitiated)
case .utility: return DispatchQueue.global(qos: .utility)
case .background: return DispatchQueue.global(qos: .background)
}
}
}
Ahora simplemente retrasa su código en el hilo principal de esta manera:
delay(bySeconds: 1.5) {
// delayed code
}
Si desea retrasar su código a un hilo diferente :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Si prefiere un Framework que también tenga algunas características más útiles, entonces consulte HandySwift . Puede agregarlo a su proyecto a través de Carthage y luego usarlo exactamente como en los ejemplos anteriores, por ejemplo:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Siempre prefiero usar la extensión en lugar de las funciones gratuitas.
Swift 4
public extension DispatchQueue {
private class func delay(delay: TimeInterval, closure: @escaping () -> Void) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
class func performAction(after seconds: TimeInterval, callBack: @escaping (() -> Void) ) {
DispatchQueue.delay(delay: seconds) {
callBack()
}
}
}
Use de la siguiente manera.
DispatchQueue.performAction(after: 0.3) {
// Code Here
}
Retrasar la llamada GCD usando asyncAfter en Swift
let delayQueue = DispatchQueue(label: "com.theappmaker.in", qos: .userInitiated)
let additionalTime: DispatchTimeInterval = .seconds(2)
Podemos retrasar como ** microsegundos , milisegundos , nanosegundos
delayQueue.asyncAfter(deadline: .now() + 0.60) {
print(Date())
}
delayQueue.asyncAfter(deadline: .now() + additionalTime) {
print(Date())
}
En Swift 4
Usa este fragmento:
let delayInSec = 1.0
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSec) {
// code here
print("It works")
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// ...
});
La dispatch_after(_:_:_:)
función toma tres parámetros:
un retraso
una cola de despacho
un bloque o cierre
La dispatch_after(_:_:_:)
función invoca el bloque o cierre en la cola de despacho que se pasa a la función después de un retraso dado. Tenga en cuenta que el retraso se crea utilizando la dispatch_time(_:_:)
función. Recuerde esto porque también usamos esta función en Swift.
Recomiendo pasar por el tutorial Raywenderlich Dispatch tutorial
En Swift 5, use en lo siguiente:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: closure)
// time gap, specify unit is second
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
Singleton.shared().printDate()
}
// default time gap is second, you can reduce it
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
// just do it!
}
use este código para realizar alguna tarea relacionada con la interfaz de usuario después de 2.0 segundos.
let delay = 2.0
let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
let mainQueue = dispatch_get_main_queue()
dispatch_after(delayInNanoSeconds, mainQueue, {
print("Some UI related task after delay")
})
Versión Swift 3.0
Después de la función de cierre, ejecute alguna tarea después del retraso en el hilo principal.
func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
onCompletion()
})
}
Llame a esta función como:
performAfterDelay(delay: 4.0) {
print("test")
}
Ahora más que el azúcar sintáctico para despachos asincrónicos en Grand Central Dispatch (GCD) en Swift.
agregar Podfile
pod 'AsyncSwift'
Entonces, puedes usarlo así.
let seconds = 3.0
Async.main(after: seconds) {
print("Is called after 3 seconds")
}.background(after: 6.0) {
print("At least 3.0 seconds after previous block, and 6.0 after Async code is called")
}
Swift 4 tiene una forma bastante corta de hacer esto:
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
// Your stuff here
print("hello")
}
Aquí está la versión síncrona de asyncAfter en Swift:
let deadline = DispatchTime.now() + .seconds(3)
let semaphore = DispatchSemaphore.init(value: 0)
DispatchQueue.global().asyncAfter(deadline: deadline) {
dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
semaphore.signal()
}
semaphore.wait()
Junto con uno asíncrono:
let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}