【发布时间】:2017-07-20 17:47:09
【问题描述】:
我阅读了这篇question and answers 和Cocoacasts blog post 并且完全理解@escaping 注释是什么。
但老实说,我完全不明白我们为什么需要它。
上述 Cocoacasts 博客文章指出:
默认情况下使闭包不转义有几个好处。 最明显的好处是性能和能力 编译器来优化你的代码。如果编译器知道一个闭包 是不可逃避的,它可以处理许多细节 内存管理的细节。
但是 Swift 编译器可以确定 @escaping 是否丢失并在这种情况下显示错误,所以如果我们从 Swift 语言中删除 @escaping 注释,那么编译器仍然可以看到闭包没有逃逸,我们可以让他应用优化在那种情况下。
这也意味着您可以使用 self 关键字而不会出现问题 非转义闭包,因为闭包在 函数返回。不需要对 self 使用弱引用 关闭。这是您免费获得的一项不错的福利。
但是如果闭包参数被标记为@escaping,我仍然可以传递使用强引用self 的闭包,并且编译器不会显示任何警告。实际上,如果在 @escaping 闭包中捕获的所有引用默认为 weak 并应用特殊关键字使其更强大,这将更有用。
我还认为@escaping 注释是通过明确声明此闭包参数不会转义函数体而使代码自我记录的方法,但调用方的目的是什么?它不限制闭包定义的方式,也不阻止调用方使用强引用。所以我剩下的就是希望调用方仔细查看函数签名并采取适当的措施(比如使用弱引用)。
所以问题是为什么我们真的需要 Swift 3 中的 @escaping 闭包,在哪些情况下我们不能没有它?
更新:
我知道不转义闭包不能传递给闭包参数标记为@escaping的函数:
func testNoEscape(f: () -> ()) {
f()
}
var storeF: (() -> ())?
func testEscaping(f: @escaping () -> ()) {
storeF = f
}
func tryPassNoEscapeToEscaping(f: () -> ()) {
testEscaping(f: f)
}
导致编译错误:
passing non-escaping parameter 'f' to function expecting an @escaping closure
但这是 @escaping 闭包带来的唯一真正限制,它看起来像是围绕自身构建的,并没有带来任何其他好处。
更新 2
虽然我在上面正确地阐述了我的想法,但我的最后一个问题是不准确的。
真正的问题是,如果编译器可以自己检测转义闭包并且@escaping 注释不对参数值施加任何限制,为什么我们需要@escaping 注释?
在我看来,如果编译器不允许我们在转义闭包时做一些坏事,比如使用self 和其他强引用,那会更有用。或者,如果转义闭包是某种特殊类型,我们必须在使用转义闭包参数调用函数时提及:
func f(c: () -> ()) { // c is escaping from f somehow
// ...
}
f escaping { // have to use `escaping` keyword
// ...
}
所以调用方不必查看f 签名就知道c 正在转义,因为如果它试图将非转义闭包作为转义闭包参数值传递,则会出现编译错误。
在当前的实现中,想要在其代码中使用f 的开发人员必须查看f 签名以了解c 会转义,这是不安全的,因为这要求任何最初编写此代码并对其进行修改的人以后一定要知道f签名的细节,这是不可靠的,这样的代码也不是自记录的。
我知道我的问题可能不适合 SO。对此感到抱歉。
如果是这样,如果我无法从使用 Swift 语言和编译器实现转义逻辑的人那里得到答案,我将在稍后关闭它。
【问题讨论】:
-
我回答你的问题了吗?
-
Alexander 我们进行了非常有益的讨论,非常感谢,但我认为这个问题只有做出这个决定的 Swift 语言开发者才能完全回答。我现在正试图联系他们,并根据我们的讨论和@matt 的回答准备 Swift 提案,我稍后会回到这个问题。
-
您已移动问题的目标帖子。由于它以目前的形式存在,它已经得到了回答。欢迎您尝试为您的新问题创建一个新帖子,但它几乎肯定会因为离题而被删除。 SO 不是讨论语言设计者决策细节的论坛。
-
堆栈溢出不是这样工作的。你不能只问一个问题,得到它的回答,然后不接受这个答案,因为它引发了一个后续问题。我已经花时间引导您彻底回答您的原始问题,直到我们达到您提出后续问题的程度开发人员的想法是他们决定的背后。
-
这个经过编辑的问题现在是句法细节和对设计师意图的猜测的毫无意义的组合。因此,我已投票结束。