dispatch_after - Swift 中的 GCD?

dispatch_after - GCD in Swift?

提问人:Kumar KL 提问时间:6/4/2014 最后编辑:Wowbagger and his liquid lunchKumar KL 更新时间:5/8/2021 访问量:294441

问:

我浏览了Apple的iBook,但找不到它的任何定义:

有人可以解释一下结构吗?dispatch_after

dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)
objective-c swift grand-central-dispatch

评论

1赞 Cœur 12/29/2018
苹果在 2018 年取消了这本书的出版。我能找到的最新档案是 2017 年 12 月的。iBook的旧链接现在只需重定向到 developer.apple.com/documentation/swift

答:

767赞 Cezary Wojcik 6/4/2014 #1

更清晰的结构概念:

dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)

dispatch_time_t是一个 .实际上,该类型别名为 ,但您应该只使用熟悉的 GCD 方法来获取队列。该块是 Swift 闭包。具体来说,定义为 ,相当于 。UInt64dispatch_queue_tNSObjectdispatch_block_t() -> Void() -> ()

用法示例:

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    print("test")
}

编辑:

我建议使用 @matt 非常好的延迟功能

编辑2:

在 Swift 3 中,将为 GCD 提供新的包装器。请参阅此处:https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

原始示例在 Swift 3 中编写如下:

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
    print("test")
}

请注意,您可以将声明编写为 并获得相同的结果,因为运算符被覆盖,如下所示(类似):deadlineTimeDispatchTime.now() + 1.0+-

  • func +(time: DispatchTime, seconds: Double) -> DispatchTime
  • func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime

这意味着,如果您不使用 ,而只写一个数字,则假定您使用的是秒。DispatchTimeIntervalenum

评论

18赞 Bill 6/5/2014
提示:因为块是函数的最后一个参数,所以你可以使用 Swift 的“尾随闭包”语法来增加可读性:dispatch_after(1, dispatch_get_main_queue()) { println("test") }
8赞 Hlung 8/28/2014
我认为使用数字可能会在这里引起很多混淆。人们会认为这是几秒钟,而实际上它是纳秒。我建议看 @brindy 关于如何正确创建这个数字的答案。1dispatch_after(1, ...
4赞 OemerA 11/8/2014
请更改为,因为它会导致混淆。人们可能会认为你不需要在 Swift 中创建dispatch_time_t1dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
4赞 Andy Ibanez 6/25/2016
Swift 3 版本似乎不起作用。它抱怨说,在线Binary operator '+' cannot be applied to operands of type DispatchTime and '_'let delayTime = DispatchTime.now() + .seconds(1.0)
10赞 Andy Ibanez 6/25/2016
重写它似乎是使其工作的唯一方法(不需要DispatchTime.now() + 1.0.seconds)
157赞 brindy 6/5/2014 #2

斯威夫特 3+

这在 Swift 3+ 中非常简单和优雅:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
    // ...
}

较早的答案:

为了扩展 Cezary 的答案(将在 1 纳秒后执行),我必须执行以下操作才能在 4 秒半后执行。

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)

编辑:我发现我的原始代码略有错误。如果不将NSEC_PER_SEC转换为 Double,则隐式键入会导致编译错误。

如果有人能提出更优化的解决方案,我很想听听。

评论

0赞 David L 7/19/2014
我收到已弃用的 API 的编译器错误。我用了。dispatch_get_current_queue()dispatch_get_main_queue()
0赞 brindy 7/19/2014
@DavidL - 谢谢,绝对是你应该使用的。将更新。dispatch_get_main_queue()
0赞 μολὼν.λαβέ 12/23/2016
我在使用 Swift 3 的 Playground 中尝试过这个,但它不起作用
0赞 brindy 12/23/2016
@GAlexander对我有用。您是否允许 Playground 无限期执行?
0赞 μολὼν.λαβέ 12/23/2016
嗯,好吧,不,我让 Run 运行了几个小时,但仍然没有打印。这是我用的。“import Dispatch import Darwin import CoreGraphics 'DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) { print(” got here “) } ”
1114赞 matt 6/20/2014 #3

我经常使用,以至于我写了一个顶级实用程序函数来使语法更简单:dispatch_after

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

现在你可以这样说话:

delay(0.4) {
    // do stuff
}

哇,一种可以改进语言的语言。还有什么比这更好的呢?


Swift 3、Xcode 8 Seed 6 更新

似乎几乎不值得打扰,现在他们已经改进了调用语法:

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

评论

2赞 Aviel Gross 8/20/2014
我只需要延迟计算的快捷方式,最终得到:func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }
4赞 matt 10/6/2014
@agf119105 如果闭包中只有一行代码,请添加另一行代码(例如)。return
2赞 matt 1/2/2015
@GastonM无关紧要。传递函数本身没有内存管理问题。
7赞 Yerk 6/19/2015
“一种可以改进语言的语言”。我不明白定义全局函数是如何改进语言的,或者为什么这在 C 中甚至不可行。也许如果你使运算符过载;)1.0 ~~ { code...}
8赞 Nikolai Ruhe 9/24/2015
不是质疑你答案的正确性,但“我经常使用dispatch_after”难道不是一种代码味道,最好通过不提供便利功能来对抗吗?
84赞 Waam 8/4/2014 #4

Matt 的语法非常好,如果你需要使块失效,你可能想使用这个:

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

用途如下

let retVal = delay(2.0) {
    println("Later")
}
delay(1.0) {
    cancel_delay(retVal)
}

学分

上面的链接似乎已关闭。来自 Github 的原始 Objc 代码

评论

1赞 HotJard 6/6/2015
具有 performSelector:afterDelay 的一项性能功能是能够取消它。只有此解决方案可以解决该问题。谢谢
0赞 matt 11/29/2015
@HotJard 请注意,它现在在 Swift 2 中可用,因此您可以取消它。performSelector:afterDelay:
0赞 HotJard 12/1/2015
@matt但它仅适用于 NSObject,不是吗?
0赞 matt 12/2/2015
@HotJard当然,但这总比没有要好。我看不出有什么问题。然而,就像这个答案一样,我已经通过编写一个基于 GCD 的可取消计时器来弥补它的损失(使用 ,因为这是你可以取消的东西)。dispatch_source_t
2赞 nontomatic 9/14/2016
非常感谢,我一直在使用 Swift 2.3。Swift 3.0 编译器现在正在抱怨,如果您更新您的答案,那就太好了!
15赞 garafajon 11/11/2014 #5

另一种方法是像这样扩展 Double:

extension Double {
   var dispatchTime: dispatch_time_t {
       get {
           return dispatch_time(DISPATCH_TIME_NOW,Int64(self * Double(NSEC_PER_SEC)))
       }
   }
}

然后你可以像这样使用它:

dispatch_after(Double(2.0).dispatchTime, dispatch_get_main_queue(), { () -> Void in
            self.dismissViewControllerAnimated(true, completion: nil)
    })

我喜欢 matt 的延迟函数,但出于偏好,我宁愿限制传递闭包。

24赞 Senseful 11/12/2015 #6

Apple 有一个 Objective-Cdispatch_after片段

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

以下是移植到 Swift 3 的相同代码段:

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + <#delayInSeconds#>) {
  <#code to be executed after a specified delay#>
}
0赞 Himanshu Mahajan 1/22/2016 #7

使用此代码在 2.0 秒后执行一些与 UI 相关的任务。

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

Swift 3.0 版本

在闭包函数之后,在主线程上延迟后执行一些任务。

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

像这样调用此函数:

performAfterDelay(delay: 4.0) {
  print("test")
}

评论

1赞 Daniel Galasko 1/22/2016
这与之前的答案几乎相同
0赞 eharo2 8/10/2018
这个答案似乎是在 2016 年初完成的,并且至少比其他 6 个答案更古老。
5赞 Alvin George 2/16/2016 #8

1) 将此方法添加为 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)
    }
}

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

紧凑的外形

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) {
    //Code here
 }
}
5赞 Suragch 5/16/2016 #9

虽然不是 OP 的原始问题,但某些相关问题已被标记为该问题的重复项,因此值得在此处包含答案。NSTimerNSTimer

NSTimerdispatch_after

  • NSTimer是更高级别的,而是更低的级别。dispatch_after
  • NSTimer更容易取消。取消需要编写更多代码dispatch_after

延迟任务NSTimer

创建实例。NSTimer

var timer = NSTimer()

以您需要的延迟启动计时器。

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

添加一个要在延迟后调用的函数(使用您用于上述参数的任何名称)。selector

func delayedAction() {
    print("Delayed action has now started."
}

笔记

  • 如果需要在操作发生之前取消操作,只需调用 。timer.invalidate()
  • 对于重复操作,请使用 .repeats: true
  • 如果您有一个不需要取消的一次性事件,则无需创建实例变量。以下几点就足够了:timer

    NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 
    
  • 在这里查看我更完整的答案。

1赞 Jeehut 6/10/2016 #10

另一个延迟代码的帮助程序,它是 100% Swift 使用的,并且可以选择选择不同的线程来运行延迟的代码:

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

现在,您只需在主线程上延迟代码,如下所示:

delay(bySeconds: 1.5) { 
    // delayed code
}

如果要将代码延迟到其他线程

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

如果您更喜欢具有一些更方便功能的框架,请查看 HandySwift。您可以通过 Carthage 将其添加到您的项目中,然后完全按照上面的示例使用它,例如:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}
9赞 Mohammad Sadiq Shaikh 7/14/2016 #11

在 Swift 3.0 中

调度队列

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

5秒后发货

    DispatchQueue.main.after(when: DispatchTime.now() + 5) {
        print("Dispatch after 5 sec")
    }
32赞 Vakas 10/24/2016 #12

Swift 3.0 & Swift 4.0 和 Swift 5.0 中最简单的解决方案

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

用法

delayWithSeconds(1) {
   //Do something
}
5赞 Himanshu Mahajan 11/3/2016 #13

Swift 3.0 版本

在闭包函数之后,在主线程上延迟后执行一些任务。

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

像这样调用此函数:

performAfterDelay(delay: 4.0) {
  print("test")
}
2赞 garg 3/30/2017 #14

这对我有用。

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

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);
});
0赞 Tim 1/3/2018 #15

现在不仅仅是 Swift 中 Grand Central Dispatch (GCD) 中异步调度的语法糖。

添加 Podfile

pod 'AsyncSwift'

然后,你可以像这样使用它。

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

评论

1赞 ingconti 1/18/2018
苹果已经为我们提供了使用GCD所需的所有信息。为什么要为豆荚、工作区等而烦恼?只需阅读有关@escaping和捕获的文档即可。这就够了。
1赞 Hardeep Singh 2/21/2018 #16

我总是更喜欢使用扩展而不是免费功能。

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

}

使用方法如下。

DispatchQueue.performAction(after: 0.3) {
  // Code Here
}
0赞 Vlady Veselinov 3/17/2018 #17

Swift 4 有一个非常简短的方法:

Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
    // Your stuff here
    print("hello")
}
2赞 Suhit Patil 4/12/2018 #18

斯威夫特 3 和 4:

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

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

用:

DispatchQueue.delay(.seconds(1)) {
    print("This is after delay")
}
1赞 Sanjay Mali 4/13/2018 #19

在 swift 中使用 asyncAfter 延迟 GCD 调用

let delayQueue = DispatchQueue(label: "com.theappmaker.in", qos: .userInitiated)
let additionalTime: DispatchTimeInterval = .seconds(2)

我们可以延迟为**微秒、秒、纳秒

delayQueue.asyncAfter(deadline: .now() + 0.60) {
    print(Date())
}

delayQueue.asyncAfter(deadline: .now() + additionalTime) {
    print(Date())
}
1赞 CrazyPro007 5/20/2018 #20
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...
});

该函数采用三个参数:dispatch_after(_:_:_:)

延迟
、调度队列
、阻塞或关闭

该函数调用调度队列上的块或闭包,该队列在给定延迟后传递给函数。请注意,延迟是使用该函数创建的。请记住这一点,因为我们也在 Swift 中使用了这个函数。dispatch_after(_:_:_:)dispatch_time(_:_:)

我建议阅读教程 Raywenderlich Dispatch 教程

3赞 Rahul Singha Roy 7/11/2018 #21

对于多个功能,请使用它。这对于将动画或活动加载器用于静态函数或任何 UI 更新非常有帮助。

DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
            // Call your function 1
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                // Call your function 2
            }
        }

例如 - 在重新加载 tableView 之前使用动画。或动画之后的任何其他 UI 更新。

*// 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()
                }
            }
1赞 BlackRock 9/10/2018 #22

Swift 4

使用此代码片段:

    let delayInSec = 1.0
    DispatchQueue.main.asyncAfter(deadline: .now() + delayInSec) {
       // code here
       print("It works")
    }

评论

0赞 Eric Aya 10/26/2018
这已经在其他答案中(例如布林迪的答案或拉胡尔的答案)......相同的语法...
1赞 Maxim Makhun 6/30/2019 #23

以下是 Swift 中 asyncAfter 的同步版本:

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

与异步一起:

let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}
5赞 Zgpeace 12/25/2019 #24

在 Swift 5 中,使用如下:

 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!
    }
-1赞 Iker Solozabal 10/9/2020 #25

若要在延迟后执行函数或代码,请使用 next 方法

DispatchQueue.main.asyncAfter(deadline: .now() + 'secondsOfDelay') {
        your code here...
    }

示例 - 在此示例中,函数将在 1 秒后执行getShowMovies

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        self.getShowMovies()
    }
0赞 Mojtaba Hosseini 5/8/2021 #26

保留 !current queue

除了这个问题的良好答案之外,您还可以考虑保留当前队列,以防止不必要的主队列操作(例如,当您尝试延迟某些异步操作时)。

func after(_ delay: TimeInterval,
           perform block: @escaping ()->(),
           on queue: DispatchQueue = OperationQueue.current?.underlyingQueue ?? .main) { // So this `queue` preserves the current queue and defaulted to the `main`. Also the caller can pass in the desired queue explicitly
    queue.asyncAfter(deadline: .now() + delay, execute: block)
}

用法:

after(3) {
    // will be executed on the caller's queue
    print(Date())
}