如何在Swift 3、4和5中编写GCD中的dispatch_after?


445

在Swift 2中,我能够dispatch_after使用大型中央调度来延迟动作:

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

但是自Swift 3起,似乎不再编译了。在现代Swift中编写此代码的首选方法是什么?


6
有关迁移过程的更多信息,请参见:https :
//swift.org/migration-guide/“分发

你的问题应该是UInt64吗?
蜂蜜

Answers:


1125

语法很简单:

// to run something in 0.1 seconds

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

请注意,上述添加seconds为a的语法Double似乎是造成混淆的原因(特别是因为我们习惯于添加nsec)。Double之所以deadline可以使用“ add seconds as ”语法,是因为a是一个,DispatchTime并且在幕后有一个+运算符将占用a Double并将其增加很多秒DispatchTime

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

但是,如果您确实想向加上毫秒,μs或nsec的整数DispatchTime,则也可以DispatchTimeInterval向加上a DispatchTime。这意味着您可以:

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

这些都无缝地起作用,因为该类中的+操作员使用了这种单独的重载方法DispatchTime

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

有人问如何取消已分派的任务。为此,请使用DispatchWorkItem。例如,这将启动一个任务,该任务将在五秒钟后触发,或者如果视图控制器被解雇并释放,deinit它将取消该任务:

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

}

请注意中使用[weak self]捕获列表DispatchWorkItem。这对于避免强大的参考周期至关重要。另请注意,这不会进行抢先取消,而只是在任务尚未启动时将其停止。但是,如果它在遇到cancel()调用时已经启动,则该块将完成其执行(除非您要手动检查isCancelled该块内部)。


5
感谢您指出这一点,实际上swift.org/migration-guide提到需要手动进行更改。
马特

1
哦对不起。这里太晚了:)。以为所有的混乱实际上都应该解决,但并没有取得进展。IMO的“简单”解决方案是一个真实的解决方案。
tobiasdm

1
@Rob我将如何取消它?谢谢。
kemicofa ghost

好的,您如何添加动态等待?例如,我有一个让数字:Float = 1.0。.now()+ .milliseconds(number)不起作用。Double(number)也没有。我不知道。
Kjell

2
DispatchTimeInterval引渡,就像.milliseconds要求Int。但是,如果只增加几秒钟,我会使用Double,例如let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }
罗布

128

斯威夫特4:

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

对于时间.seconds(Int).microseconds(Int)并且.nanoseconds(Int)也可使用。


7
.milliseconds比Double好。
DawnSong

5
非常好。其他说明:您也可以使用任何其他DispatchTimeInterval枚举值。case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern

@RobMacEachern,谢谢,这是一个好建议,我将其添加到答案中。
Sverrisson

2
.milliseconds is better than Double. -我想要一件T恤;)。
克里斯·普林斯

58

如果您只想要延迟功能

斯威夫特4&5

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

您可以像这样使用它:

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

DispatchQueue.main.asyncAfter(deadline:)不起作用。它说它不会重载其父类中的任何方法。
Fabrizio Bartolomucci,

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)更简单。
DawnSong


5

接受答案的风格略有不同。

斯威夫特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

斯威夫特4

您可以在DispatchQueue上创建扩展,并添加在DispatchQueue内部使用asyncAfter函数的函数延迟

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

和使用

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

2
这与@rockdaswift的答案有何不同?
brandonscript

正如我所提到的,它将asyncAfter包裹在performAfter函数内部,该函数将delay作为参数,使用performAfter(delay:2){}
Suhit Patil

默认情况下,closure参数是不转义的,@ escaping表示可能会转义closure参数。在闭包中添加了@逃逸参数以节省潜在的崩溃。
Suhit Patil

3

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

我强烈建议使用Xcode工具转换为Swift 3(“编辑”>“转换”>“转换为当前Swift语法”)。它为我抓住了


3

在Swift 4.1和Xcode 9.4.1中

简单的答案是...

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

3
不确定这与接受的答案有何不同?
brandonscript

3

Swift 5及以上

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

1

没有一个答案提到在非主线程上运行,因此加了2美分。

主队列上(主线程)

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

要么

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

全局队列上(非主线程,基于指定的QOS)。

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

要么

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

0

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

5
不确定这与接受的答案有何不同?
brandonscript


0

尝试这个

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

不确定这与受影响的答案有何不同?
brandonscript
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.