如何在本地闭包中调用非逃逸闭包?

5

我有一个函数,看起来像这样:

func test(closure: () -> ()) {
    let localClosure = { closure() }

    localClosure()
}

这只是一个例子,没有充分反映我遇到的问题,在这里,我显然可以直接调用closure

很明显,在上面的代码中,closure不能逃逸。但是,我却得到了如下错误:

闭包对非逃逸参数“closure”的使用可能会导致其逃逸

如果localClosure以某种方式逃逸,那么我就能理解这个错误,但它并没有逃逸。我甚至尝试用@noescape注释localClosure(即使在Swift 3中已经废弃了该属性),根据我收到的警告:

@noescape是默认值,已被弃用

如果localClosure默认是不逃逸的,那么为什么另一个非逃逸闭包不能进入其中?或者这是编译器的一个bug/限制吗?


1
可能相关的问答(至少与编译器关于localClosure默认为@noescape的错误警告有关,而实际上它并不是):为什么在Swift 3中闭包默认都是非逃逸的,但仍需要显式的self - Hamish
@Hamish,我认为目标线程实际上是这个线程的完美副本标记(即使它是一个略微不同的问题,目标的答案也回答了这个问题):一旦明确非参数闭包实际上默认是@escaping,整个讨论(和之前的答案)就会崩溃。投票标记为重复! - dfrib
@dfri 嗯...实际上仔细想想,我同意 - 鉴于答案基本上是“*localClosure实际上是@escaping,目前没有(非弃用的)标记它为非逃逸的方法*”。 - Hamish
1个回答

5

非参数闭包默认为@escaping

"如果localClosure默认为非逃逸,那么为什么..."

根据下面评论区的讨论(感谢@Hamish),我们可以陈述关于Swift 3.0中非参数闭包的如下事实:

  • 与一个人们可能认为的相反,它们默认是@escaping。由于在Swift 3中@noescape已被弃用(见例如Xcode 8 release notesSwift evolution proposal SE-0103),这意味着非参数闭包不能通过使用弃用的方法来使其成为非逃逸闭包。
  • 正如以下演进线程所描述的那样,对于非参数闭包缺少@noescape属性是一个缺少的特性(在Swift 2.2中不受限制,有些退化了),但不一定会在将来实现(如果我理解Apple dev. Jordan Rose在链接的演进线程中的答案)。
  • 然而,我们仍然可以将弃用的@noescape属性应用于非参数闭包以使其成为非逃逸闭包,但会遇到一个不正确的警告提示(如下所示,重点是我的),这已经被@Hamish报告为一个错误,见bug report SR-2969

    "@noescape 是默认的并且已经被弃用"

总之,localClosure@escaping,这自然意味着它不能包装test(...)中的非逃逸闭包参数closure

[†] 非参数闭包指所有不是函数参数的闭包,即未作为函数参数提供的闭包。


顺便说一句,你可能已经知道的一件事:如果我们希望像你的例子中那样处理/包装closure,我们自然可以将其标记为@escaping

func test(closure: @escaping () -> ()) -> () -> () {
    let escapingLocalClosure = { closure() }
    return escapingLocalClosure
}

谢谢,基本上如果我将闭包分配给一个局部变量,编译器会假定它可能超出所在函数的生命周期,从而使其逃逸。很遗憾它无法推断它的使用方式,因为我认为原始示例应该被允许。也许将来会有所改进! - Robert
所有这样的闭包,例如localClosure,默认情况下都是@escaping的吗?即使我们没有将它们注释为这样。这是否可以被认为是有点错误的行为? - dfrib
@dfri 但编译器可以检测到 localClosure 是否可能逃逸,如果您尝试让它逃逸(例如现在请参见此代码片段 :)),编译器将会发出错误。对我来说,这表明了框架已经就位,使得编译器能够将局部变量函数视为 @noescape (默认情况下它们确实是 @escaping - 如果没有 @noescape 尝试返回它),但目前仍存在一些问题(错误?)在删除 @noescape 的同时转向 @escaping 上。在我看来,我们应该被允许这样做。 - Hamish
@Hamish 感谢您的所有反馈;由于另一个问题(><),我需要暂停 SO:ing 几个小时,因此我需要回来处理这个问题。但是 1) 提示添加(已弃用的!)@noescape 的警告消息在本例中是完全错误的(_"@noescape 是默认值,..."_),因为这显然只适用于作为函数参数的闭包。2) 如果“本地”闭包(例如 localClosure)确实打算默认为 @escaping,那么 @noescape 的弃用是否过早?例如,OP 的示例正是 @noescape 的使用情况。谢谢! - dfrib
1
有趣的是,在Swift 2.3中,你甚至不能将本地变量函数标记为@noescape,这意味着Swift团队同时允许在这种情况下使用@noescape并将其弃用:P 我将开始撰写一个错误报告,看看Swift团队对此有何看法。 - Hamish
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接