如何在 Swift 3(引擎盖下)中实现转义闭包?它们是否像objective-c那样隐式地block_copied/retained?

AyB*_*Bay 5 closures objective-c objective-c-blocks swift swift3

我想了解转义闭包在 Swift 3 中是如何工作的?来自 Objective-C 世界,对于闭包可以逃避其封闭函数的返回的场景,您必须按照以下方式做一些事情:

@property (nonatomic, copy/strong) void (^callback)(NSData *rawData);

-(BOOL)someFunctionThatConsumesABlock:(void (^)(NSData *rawData))block 
{
  if (callback) {
      self.callback = block;
      return YES;
  }
  return NO;
}

-(void)someFunctionThatExecutesAtSomeLaterPoint
{
  NSData *data = [self bytesFromSomeProcess];
  self.callback(data);
}
Run Code Online (Sandbox Code Playgroud)

基本上在objective-C的上述代码中,消耗块的对象执行堆复制并强保留块。这种强保留是有道理的,因为块也需要在它作为参数传递的函数范围之外停留(因为它是一个转义闭包/块)。

在 Swift 3 中,似乎不再需要上述内容。所有需要做的只是将闭包注释为“转义”。那么 Swift 3 在幕后做了什么?它和Objective-C做同样的事情吗?从本质上讲,Swift 3 中的转义闭包是否也会被它们传递的对象隐式强保留?我不知道该语言还有什么方法可以实现这一点并使闭包“保持不变”。

jtb*_*des 3

当您将闭包保存为属性或其他方式时,闭包确实会被隐式保留(强保留)。来自 Swift 编程语言,自动引用计数

\n\n
\n

\xe2\x80\xa6 闭包与类一样,都是引用类型。当您将闭包分配给属性时,您正在分配对该闭包的引用。

\n
\n\n

(这就是捕获列表存在的原因:帮助避免意外的保留周期。)

\n\n

但是,我认为您可能误解了(或旧版本 Swift 中@escaping缺少)的目的。@noescape这不会自动为您保存关闭。相反,它只是向调用者表明您可以保存闭包(闭包可能会逃脱函数的执行)。这允许编译器执行额外的优化,例如跳过保留。它还允许调用者self.在闭包内省略:

\n\n
class Foo {\n    var x = 3\n\n    func test() {\n        [1, 2, 3].map { $0 + x }\n        // `self.x` is not necessary, because the map function\'s\n        // closure is non-escaping\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

(如果您有兴趣了解 的幕后情况@escaping,我不知道此类信息的明确来源,但您可能会在有关 SIL 的演讲中找到一些有用的内容,开源项目中的SIL.rst文档,也许还有 WWDC16 中的Understanding Swift Performance。)

\n

  • 我认为你误解了@escaping 的作用。它**不会**自动为您保存给定的闭包。它只是告诉您的呼叫者您*可能*保存它。*非*转义闭包的优点是允许调用者隐式使用“self”,因为编译器知道它不会导致保留循环。 (2认同)