Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
703 views
in Technique[技术] by (71.8m points)

swift4 - How do I write dispatch_after GCD in Swift 3, 4, and 5?

In Swift 2, I was able to use dispatch_after to delay an action using grand central dispatch:

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

But this no longer seems to compile since Swift 3. What is the preferred way to write this in modern Swift?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The syntax is simply:

// to run something in 0.1 seconds

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

Note, the above syntax of adding seconds as a Double seems to be a source of confusion (esp since we were accustomed to adding nsec). That "add seconds as Double" syntax works because deadline is a DispatchTime and, behind the scenes, there is a + operator that will take a Double and add that many seconds to the DispatchTime:

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

But, if you really want to add an integer number of msec, μs, or nsec to the DispatchTime, you can also add a DispatchTimeInterval to a DispatchTime. That means you can do:

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

These all seamlessly work because of this separate overload method for the + operator in the DispatchTime class.

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

It was asked how one goes about canceling a dispatched task. To do this, use DispatchWorkItem. For example, this starts a task that will fire in five seconds, or if the view controller is dismissed and deallocated, its deinit will cancel the task:

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

}

Note the use of the [weak self] capture list in the DispatchWorkItem. This is essential to avoid a strong reference cycle. Also note that this does not do a preemptive cancelation, but rather just stops the task from starting if it hasn’t already. But if it has already started by the time it encounters the cancel() call, the block will finish its execution (unless you’re manually checking isCancelled inside the block).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...