dispatch_after – GCD在swift中?
我已经通过了苹果的iBook ,并找不到它的任何定义:
有人可以解释dispatch_after
的结构吗?
dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)
对结构更清晰的概念:
dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)
dispatch_time_t
是一个UInt64
。 dispatch_queue_t
实际上是一个types别名到一个NSObject
,但你应该使用你熟悉的GCD方法来获得队列。 该块是一个Swift结束。 具体来说, dispatch_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的非常好的delay
function 。
编辑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") }
请注意,您可以将deadlineTime
声明写为DispatchTime.now() + 1.0
并获得相同的结果,因为+
操作符被覆盖如下(类似于-
):
-
func +(time: DispatchTime, seconds: Double) -> DispatchTime
-
func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime
这意味着如果您不使用DispatchTimeInterval
enum
并只写一个数字,则假定您正在使用秒。
我经常使用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) }
马特的语法是非常好的,如果你需要使块无效,你可能想要使用这个:
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) }
学分
上面的链接似乎是closures的。 来自Github的原始Objc代码
为了扩大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,则隐式键入会导致编译错误。
如果有人可以提出一个更优化的解决scheme,我会热衷于听到它。
==更新Swift 3 ==
这在Swift 3中是超级简单而优雅的:
DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) { // ... }
苹果有一个Objective-C的dispatch_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#> }
另一种方法是像这样扩展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) })
我喜欢马特的延迟function,但只是偏好我宁愿限制周围的封闭。
Swift 3.0和Swift 4.0中最简单的解决scheme
func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) { DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { completion() } }
用法
delayWithSeconds(1) { //Do something }
在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") }
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 })
//紧凑forms
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) { //Code here }
}
Swift 3.0版本
在closuresfunction之后,在主线程上执行一些任务。
func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: { onCompletion() }) }
像这样调用这个函数:
performAfterDelay(delay: 4.0) { print("test") }
虽然不是OP的原始问题,但某些与NSTimer
相关的问题已经被标记为这个问题的重复,所以在这里值得包含NSTimer
答案。
NSTimer
vs dispatch_after
-
NSTimer
在dispatch_after
更高级别,更低级别。 -
NSTimer
更容易取消。 取消dispatch_after
需要编写更多的代码 。
用NSTimer
延迟任务
创build一个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
使用repeats: true
。 -
如果您有一次性事件而不需要取消,则不需要创build
timer
实例variables。 以下就足够了:NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false)
-
在这里看到我更完整的答案。
另一个帮助你延迟使用100%Swift的代码,还可以select不同的线程来运行你的延迟代码:
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 }
如果你更喜欢一个框架 ,也有一些更方便的function,然后检查HandySwift 。 您可以通过Carthage将其添加到您的项目中,然后像上面的示例中那样使用它,例如:
import HandySwift delay(bySeconds: 1.5) { // delayed code }
这对我有效。
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)") }
Objective-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); });
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版本
在closuresfunction之后,在主线程上执行一些任务。
func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: { onCompletion() }) }
像这样调用这个函数:
performAfterDelay(delay: 4.0) { print("test") }