在将闭包传递给 API 时,你需要考虑相对于代码中其他代码调用闭包的
时间
。在同步 API 中,闭包的调用结果会在你传递闭包后立即可用。在异步 API 中,该结果需要等待一段时间后才会可用;这一差别影响闭包“内”
代码和闭包“后”
代码的编写方式。
以下示例定义了两个函数:
now(_:)
和
later(_:)
。你可以用相同的方式调用这两个函数:利用后置闭包而不使用其他参数。
now(_:)
和
later(_:)
都接受闭包并调用它,但
later(_:)
则会等几秒才调用其闭包。
let
queue =
DispatchQueue
(label:
"com.example.queue"
)
func
now
(
_
closure:
()
->
Void
) {
closure()
func
later
(
_
closure: @escaping
()
->
Void
) {
queue.asyncAfter(deadline: .now() +
2
) {
closure()
now(_:)
和
later(_:)
函数代表着你在 App 框架内接受闭包的方法中最常遇到的两种 API 类别:类似于
now(_:)
的同步 API,和类似于
later(_:)
的异步 API。
由于调用闭包可能会改变 App 的局部和全局状态,因此在编写传递闭包后的代码时需要仔细考量闭包被调用的
时间
。即便是打印一组字母这样简单的任务,也可能会受到闭包调用时机的影响:
print
(
"D"
)
let
semaphore =
DispatchSemaphore
(value:
0
).wait(timeout: .now() +
10
)
在使用接受闭包的 API 时,你经常需要考虑这种基于时间的执行问题。在许多情形中,只有一种调用顺序对你的 App 来说是正确的,因此务必要根据你所使用的 API,仔细思考你的 App 将处于的状态。你可以结合使用 API 名称、参数名称以及相关的文档来判断 API 是同步还是异步的。
一个常见的时序错误是预计异步调用的结果可在同步调用代码中使用。例如,上文中的
later(_:)
方法与
URLSession
(英文)
类的
data
Task(with:
completion
Handler:)
(英文)
方法相当,后者也是异步的。你应当避免下面这样的时序场景:在 App 的
view
Did
Load()
(英文)
方法中调用
data
Task(with:
completion
Handler:)
方法,并试图在作为完成处理程序传递的闭包外面使用其结果。
lines.forEach { line in
file.write("\(line)\n".data(using: .utf8)!)
file.closeFile()
static var lotteryWinHandler: (() -> Void)?
@discardableResult static func pickWinner(guess: Int) {
print("Running the lottery.")
if guess == Int.random(in: 0 ..< 100_000_000), let winHandler = lotteryWinHandler {
winHandler()
return true
return false
for _ in 1..10 {
Lottery.pickWinner(guess: myLuckyNumber)
if amountOwed > 0 {
fatalError("You need to pay your bills before proceeding.")
Apple Developer Program
Apple Developer Enterprise Program
App Store Small Business Program
MFi Program (英文)
News Partner Program (英文)
Video Partner Program (英文)
安全赏金计划 (英文)
Security Research Device Program (英文)