为什么@noescape在需要时不会自动应用于Swift闭包?

时间:2016-01-12 20:43:59

标签: ios swift closures

我想知道为什么标签noescape没有被自动检测到,需要明确应用。

实际上,它在编译时以某种方式检测到,因为尝试将带有转义闭包的@noescape标记添加到func会导致错误。

所以问题是为什么......为什么noescape需要明确添加,Apple没有创建它以在需要时自动添加?

2 个答案:

答案 0 :(得分:2)

修改:Swift 3在此处进行了一些更改:

  • Non-escaping closures are the default.现在,如果要要求非转义闭包,则不必将@noescape应用于将闭包作为参数的函数声明。 (相反,如果 计划在函数返回后存储闭包,则必须应用@escaping。)
  • 封闭is now part of the function type declaration的“逃避”。因此,不是看起来像@escaping completionHandler: (Bool, Error) -> Void的参数,而是completionHandler: @escaping (Bool, Error) -> Void

有点难以重写我的整个答案以反映这一点,所以我现在暂时将它留在这里...继续阅读为什么在逃避后,只记得反转noescape /逃避声明。 :)或者阅读Swift 3版本的 Swift编程语言中的Escaping Closures

@noescape不仅仅是编译器优化的提示;它是函数声明向调用者提供的接口的一部分。函数的参数是声明@noescape还是允许转义闭包会改变该函数的调用者如何将它们作为参数传递的闭包写入。

例如,给定函数(来自SequenceType):

func filter(@noescape includeElement: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.Generator.Element]

如果我想根据需要在self上调用方法的某些条件过滤集合,我知道我可以安全地执行此操作而不必担心闭包是否会捕获self并创建保留周期。

// horribly contrived example
class Foo {
    var things: [Thing]
    func isCurrentlyAwesomeThing(thing: Thing) -> Bool { /*...*/ }
    func thingsThatAreAwesomeRightNow() -> [Thing] {
        return things.filter {
            return isCurrentlyAwesomeThing($0)
        }
    }
}

如果filter允许转义关闭,则调用isCurrentlyAwesomeThing()的闭包将捕获self。 (因此要求使用显式self.前缀调用该方法。)如果filter的实现实际上将闭包保存到该函数的运行时之外,则会出现内存泄漏,因为闭包保留selfself保留其filter函数收到闭包的数组。

当您调用其闭包参数未声明为@noescape的函数时,您必须考虑到这种可能性。这就是为什么你看到在闭包内添加[weak self]捕获列表和强重新声明的调用,以确保在闭包期间不会释放self。 (或者,如果你有理由确定闭包的持续时间不会超过[unowned self],则至少要self。)

如果闭包参数不能被装饰为@noescape(或者由于缺少装饰器而无法转义),你不知道什么时候调用一个带有闭包的函数是否必须关注封闭捕获的内容。

答案 1 :(得分:0)

如果您使用任何函数参数作为noescape而无法存储在另一个闭包中,则无法使用dispatch_asynch并且您无法从noescape调用非noescape闭包关闭

在闭包中应该使用noescape属性的范围