当您执行异步工作时,它可能转义当前范围原因,根据定义,它不会等待其完成。
这也是为什么术语completion用于那些闭包在异步代码中用于传递结果的原因。
这是一个示例,假设queue 是DispatchQueue 并且someHugeWork() -> R 是一个同步 函数,它返回一个结果R:
func doItSync() -> R {
var result: R!
queue.sync {
result = someHugeWork()
}
// the function has to wait for the code submitted synchronously
// on the queue.
return result
}
func doItAsync(completion: @escaping (R) -> Void) {
queue.async {
let result = someHugeWork()
completion(result)
}
// work was submitted asynchronously on the queue, hence the function
// just returns, therefore the completion closure might escape this
// scope.
}
现在,显然在这种情况下doItSync() 并没有真正意义,因为它会阻塞当前线程,直到someHugeWork() 在不同的队列上执行。这也是您可以从其作用域返回结果的原因:它等待它。我添加这个只是为了让您了解同步代码和返回结果的异步代码之间的区别。
另一方面,doItAsync(completion:) 只是立即返回,而无需等待 someHugeWork() 在另一个队列上完成,因此它不会阻塞当前线程。
由于您已在队列中异步 提交此工作,因此您现在无法控制何时执行,这也是为什么您从someHugeWork() 返回结果的唯一方法是使用 completion 闭包,它必须在 work item 内捕获 提交异步...如果您考虑一下就好像这样的 completion 闭包被存储以供队列在稍后执行 异步 提交的工作项时检索(您在问题中说过你确实有存储闭包的概念)。
现在让我们也看看一个不需要 escaping 闭包的方法,这将是Sequence 的forEach(_:) 方法的body 参数:这个方法执行给定的关闭序列的每个元素,就像这样做:
func forEach(_ body: (Element) -> Void) {
for element in self {
body(element)
}
// the function returns only after the for-in loop has completed,
// hence body closure only executes in the scope of this method,
// thus it never escapes.
}
(为简单起见,我省略了抛出注释)
在这种情况下,body 闭包永远不会逃脱forEach(_:) 方法的作用域,因此它不需要注释为@escaping:它只会在for-in 循环内执行,该循环必须在方法之前完成可以返回。