Swift:为什么需要“@escaping”?

Swift: why is `@escaping` needed?

我的意思是,如果编译器强制添加符号,那么在我看来它已经理解它是一个转义块。

那为什么要强制程序员加上呢?

丹克斯

它是您函数 public API 的一部分。从非转义闭包更改为转义闭包是一项重大更改。

注释的存在是为了明确这一点。

这是一个重大更改的示例:

func f(_ closure: /* @escaping */ () -> Void) {
    closure()
}

struct AStruct {
    var i = 0
    
    mutating func mutate() {
        f { i += 1 }
    }
}

如果f采用非转义闭包,一切都很好。但是如果你做到 @escaping,你会得到 error: escaping closure captures mutating 'self' parameter

还有一些其他可能的错误与闭包捕获相关,这些错误能够有效地将结构变成引用类型(从而破坏任何来自值类型的保证)

您需要 @escaping 来区分闭包类型,因为:

  1. 并非所有闭包都是“escaping”(当然,我们对异步方法使用转义模式,但对于函数式编程模式,闭包通常是非转义的);和

  2. 编译器可以对非转义闭包进行某些优化(例如,它如何处理幕后的内存调用),而转义闭包则不可能。

最重要的是,@escaping 限定符是让编译器知道它可以进行哪些优化所必需的。

此外,我们应该始终能够查看接口并轻松推断出闭包是否正在转义。如果没有一些限定符,就很难推理一个人的代码。


FWIW,因为非转义闭包不会引入与转义闭包相同的强引用循环风险,与转义闭包相比,编译器允许对非转义闭包中的属性进行更自然的引用。通过 including/excluding @escaping 限定符,编译器还知道如何处理闭包内的 self 引用。


在遥远的过去,闭包默认为转义,我们不得不用 @noescape 注释非转义的闭包。然后,作为 SE-0103 的结果,闭包的默认值更改为非转义,从而弃用了旧的 @noescape 注释,但要求我们使用 @escaping.[=19 注释转义闭包=]