如何不再害怕延迟并爱上Swift 3、4和5中的GCD
\n\n你是否曾经写过一段代码,本应在三秒后执行,却立即触发了?或者相反,你等了很久,明明只计划了几毫秒?熟悉吗?那你一定遇到过dispatch_after——这是Grand Central Dispatch (GCD)中最流行但也是最棘手的工具之一。
\n\n以前,在Swift 2和Objective-C时代,这个方法看起来像咒语:dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(nanoseconds)), dispatch_get_main_queue())。但随着Swift 3、4和5的到来,GCD的API被完全重写了。现在这不仅仅是名称的改变——这是一种全新的思维方式。在本文中,我们将探讨如何在所有当前版本中编写Swift中的dispatch_after,破除迷思,并提供现成的解决方案。开始吧!
dispatch_after发生了什么?一段简短的痛苦历史
\n在Swift 3之前,开发者使用C风格的GCD:
\nlet delay = dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC)))\ndispatch_after(delay, dispatch_get_main_queue()) {\n print("来自过去的问候!")\n}\n这虽然有效,但看起来很笨重。在Swift 3中,Apple决定将GCD“Swift化”,将其转变为面向对象的API。现在dispatch_after变成了DispatchQueue结构体的方法asyncAfter。这不仅仅是表面上的修整——这是一个需要理解的全新语法。
基本配方:Swift 3、4和5中的asyncAfter
\n无论Swift版本(3、4或5),dispatch_after的基本语法现在看起来都一样。以下是在主线程上延迟执行的最简单示例:
\n\nlet delayInSeconds: Double = 2.0\nDispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {\n print("2秒过去了!")\n}\n关键点:
\n- \n
- DispatchQueue.main — 主线程(UI)。 \n
- .now() + delayInSeconds — 创建相对于当前时刻的截止时间(deadline)。 \n
- asyncAfter — 在指定时间后异步执行。 \n
这段代码在Swift 3.0、4.0和5.0中无需修改即可运行。神奇之处在于,Apple自2016年以来一直保持这个API的稳定。
\n\n那dispatch_time呢?处理纳秒
\n有时你需要以纳秒为单位指定延迟(例如,用于精确计时器或游戏逻辑)。在新API中,使用DispatchTime:
\nlet nanoseconds: Int = 1_500_000_000 // 1.5秒\nlet deadline = DispatchTime.now() + .nanoseconds(nanoseconds)\nDispatchQueue.global().asyncAfter(deadline: deadline) {\n print("1.5秒后在后台线程执行")\n}\n你还可以使用现成的常量:.seconds(5)、.milliseconds(500),甚至.microseconds(1000)。这比乘以NSEC_PER_SEC更方便。
关于Swift中dispatch_after的常见问题(和答案!)
\n在Stack Overflow上,这个问题已经积累了数千次浏览。以下是新手最容易踩的坑。
\n\n1. 为什么我的代码立即执行,而不是延迟执行?
\n最常见的错误是混淆asyncAfter和async。如果你忘记指定deadline:,或者写了DispatchQueue.main.async { ... },代码会立即执行。第二个原因是传递了负值。.now() + (-5.0)表示过去的时间,GCD会立即执行该块。
2. 如何取消dispatch_after?
\n与Timer不同,asyncAfter没有内置的cancel()方法。但有两种解决方案:
- \n
- 取消标志:使用
Dispatc