逃离Swift中的闭包

Nik*_*har 41 closures swift

我是Swift的新手,当我遇到逃脱闭合时,我正在阅读手册.我根本没有得到手册的描述.有人可以用简单的语言向我解释一下Swift中有什么逃避封锁.

Swe*_*per 68

考虑这个课程:

class A {
    var closure: (() -> Void)?
    func someMethod(closure: @escaping () -> Void) {
        self.closure = closure
    }
}
Run Code Online (Sandbox Code Playgroud)

someMethod 将传入的闭包分配给类中的属性.

现在又来了另一堂课:

class B {
    var number = 0
    var a: A = A()
    func anotherMethod() {
        a.someMethod { self.number = 10 }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我调用anotherMethod,闭包{ self.number = 10 }将存储在实例中A.由于self在闭包中捕获,因此实例A也将对其进行强有力的引用.

这基本上是逃脱关闭的一个例子!

你可能想知道,"什么?那么关闭从哪里逃脱,到了?"

闭包从方法的范围逃逸到类的范围.它可以在以后调用,即使在另一个线程上!如果处理不当,这可能会导致问题.

为避免意外转义闭包并导致保留周期和其他问题,请使用以下@escaping属性:

class A {
    var closure: (() -> Void)?
    func someMethod(closure: () -> Void) {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在如果你试着写@escaping,它就不会编译!

更新:

在Swift 3中,默认情况下所有闭包参数都无法转义.必须添加self.closure = closure属性才能使闭包能够从当前作用域中转义.这为您的代码增加了更多的安全性!

class A {
    var closure: (() -> Void)?
    func someMethod(closure: @escaping () -> Void) {
        self.closure = closure
    }
}
Run Code Online (Sandbox Code Playgroud)


LC *_*C 웃 42

我将以更简单的方式进行.

考虑这个例子:

func testFunctionWithNonescapingClosure(closure:() -> Void) {
        closure()
}
Run Code Online (Sandbox Code Playgroud)

上面是一个非转义闭包,因为在方法返回之前调用闭包.

考虑使用异步操作的相同示例:

func testFunctionWithEscapingClosure(closure:@escaping () -> Void) {
      DispatchQueue.main.async {
           closure()
      }
 }
Run Code Online (Sandbox Code Playgroud)

上面的示例包含一个转义闭包,因为闭包调用可能在函数返回后由于异步操作而发生.

 var completionHandlers: [() -> Void] = []
 func testFunctionWithEscapingClosure(closure: @escaping () -> Void) {
      completionHandlers.append(closure)
 }
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,你可以很容易地意识到闭合是在函数体外移动所以它需要是一个逃逸的闭包.

在Swift 3中添加了转义和非转义闭包以进行编译器优化.您可以搜索nonescaping闭包的优点.


sta*_*tan 16

我发现这个网站对此非常有帮助 简单的解释是:

如果闭包作为参数传递给函数,并且在函数返回后调用它,则闭包就会转义.

阅读我上面传递的链接!:)


小智 10

默认情况下,闭包是非转义的。为了简单理解,您可以将 non_escaping 闭包视为局部闭包(就像局部变量一样),将转义视为全局闭包(就像全局变量一样)。这意味着一旦我们从方法体中出来,non_escaping 闭包的范围就丢失了。但是在转义闭包的情况下,内存在内存中保留了闭包。

***当我们在方法中的任何异步任务中调用闭包时,或者在调用闭包之前方法返回时,我们只是使用转义闭包。

非转义闭包:-

func add(num1: Int, num2: Int, completion: ((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Background")
        completion(num1 + num2) // Error: Closure use of non-escaping parameter 'completion' may allow it to escape
    }
    return num1
}

override func viewDidLoad() {
    super.viewDidLoad()
    let ans = add(num1: 12, num2: 22, completion: { (number) in
        print("Inside Closure")
        print(number)
    })
    print("Ans = \(ans)")
    initialSetup()
}
Run Code Online (Sandbox Code Playgroud)

由于它是 non_escaping 闭包,一旦我们从 'add' 方法中出来,它的作用域就会丢失。completion(num1 + num2) 永远不会调用。

逃脱关闭:-

func add(num1: Int, num2: Int, completion: @escaping((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Background")
        completion(num1 + num2)
    }
    return num1
}
Run Code Online (Sandbox Code Playgroud)

即使方法返回(即我们离开方法范围),闭包也会被调用。enter code here


bla*_*arl 5

斯威夫特4.1

从语言参考:Swift编程语言(Swift 4.1)的属性

苹果escaping清楚地解释了该属性。

将此属性应用于方法或函数声明中的参数类型,以指示可以存储该参数的值以供以后执行。这意味着允许该值超过调用的生存期。具有转义类型属性的函数类型参数需要显式使用self。用于属性或方法。有关如何使用转义属性的示例,请参见转义闭包

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
Run Code Online (Sandbox Code Playgroud)

someFunctionWithEscapingClosure(_:)函数将闭包作为其参数,并将其添加到在函数外部声明的数组中。如果未使用标记该函数的参数@escaping,则会出现编译时错误。

当闭包作为函数的参数传递给闭包时,闭包被认为是对函数的转义,但是在函数返回后会被调用。当您声明一个将闭包作为其参数之一的函数时,可以在参数的类型之前编写@escaping,以指示允许对闭包进行转义。