了解转义闭包 Swift

Understanding escaping closures Swift

需要有关转义闭包的帮助。

我理解转义闭包的定义是If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.

我认为在某些情况下需要转义闭包。

  1. 将外部变量设置为传递闭包

    我理解这一点,因为变量甚至在函数 returns 之后仍然存在。

  2. 异步操作

    诸如Dispatch.main.async{}Network Request之类的东西。这是我不太明白的部分。我知道 Dispatch.main.async{} 在不同的线程上运行,异步操作需要一段时间才能完成,因此得名异步。但是,我无法理解这些操作如何超出函数的范围。

当您调用 DispatchQueue.main.async{} 时,您正在创建一个闭包(Objective-C 中的 block),它被发送到给定的调度队列。然后,您的函数将在 async 调用之后继续 运行ning,并可能在该块有机会在 DispatchQueue 上 运行 之前结束。闭包“逃”了!

当块到达其DispatchQueue

的前面时,块中的代码实际上将是运行

async 并不意味着操作会花费很长时间,它意味着这个函数(调用 async 的函数)不会暂停等待块完成。

(如果您确实想暂停并等待操作完成,您可以调用 sync 而不是 async)。

当你做异步工作时,它可能逃避当前范围,根据定义,它不会等待它的完成。 这也是为什么术语 completion 用于 closuresasynchronous 代码中用于交付结果。

这是一个例子,假设 queue 是一个 DispatchQueue 并且 someHugeWork() -> R 是一个 同步 函数,结果是 return 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() 在不同的队列上执行。这也是为什么你可以 return 从它的作用域中得到结果的原因:它等待它。我添加这个只是为了让您看到同步代码和异步代码之间的区别return结果。

另一方面,doItAsync(completion:) 只需立即 returns,而不必等待 someHugeWork() 在不同的队列上完成,因此它不会阻塞当前线程。
由于您已经在队列中 异步 提交了这项工作,因此您现在无法控制它何时执行,这也是为什么您只能通过 return someHugeWork() 是通过使用 completion 闭包,必须在 work itemcaptured提交异步…如果你想一想,就好像这样完成闭包被存储以供队列在某个时间检索稍后它将执行提交的工作项异步(你在你的问题中说你确实得到了存储闭包的概念)。

现在让我们再看一个不需要 escaping 闭包的方法,它是 Sequencebody 参数 forEach(_:) 方法:此方法对序列的每个元素执行给定的闭包,就像在做:

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 内部执行在方法可以 return.

之前必须完成的循环