Consider this class:
考虑一下这个班级:
class A {
var closure: (() -> Void)?
func someMethod(closure: @escaping () -> Void) {
self.closure = closure
}
}
someMethod
assigns the closure passed in, to a property in the class.
某些方法将传入的闭包赋值给类中的属性。
Now here comes another class:
现在又来了一个类:
class B {
var number = 0
var a: A = A()
func anotherMethod() {
a.someMethod { self.number = 10 }
}
}
If I call anotherMethod
, the closure { self.number = 10 }
will be stored in the instance of A
. Since self
is captured in the closure, the instance of A
will also hold a strong reference to it.
如果我调用另一个方法,闭包{self.number=10}将被存储在A的实例中。由于自身被捕获在闭包中,因此A的实例也将包含对它的强引用。
That's basically an example of an escaped closure!
这基本上就是一个逃逸关闭的例子!
You are probably wondering, "what? So where did the closure escaped from, and to?"
你可能想知道,“什么?那么关闭是从哪里逃出来的,又逃到了哪里呢?”
The closure escapes from the scope of the method, to the scope of the class. And it can be called later, even on another thread! This could cause problems if not handled properly.
闭包从方法的作用域转移到类的作用域。它可以在以后调用,甚至可以在另一个线程上调用!如果处理不当,这可能会导致问题。
By default, Swift doesn't allow closures to escape. You have to add @escaping
to the closure type to tell the compiler "Please allow this closure to escape". If we remove @escaping
:
默认情况下,SWIFT不允许闭包转义。您必须将@ESCRING添加到闭包类型中,以告诉编译器“请允许这个闭包转义”。如果我们去掉@ESCRING:
class A {
var closure: (() -> Void)?
func someMethod(closure: () -> Void) {
}
}
and try to write self.closure = closure
, it doesn't compile!
并尝试编写self.闭包=闭包,它不能编译!
I am going in a more simpler way.
我要用一种更简单的方式。
Consider this example:
请考虑以下示例:
func testFunctionWithNonescapingClosure(closure:() -> Void) {
closure()
}
The above is a non-escaping closure because the closure is
invoked before the method returns.
Consider the same example with an asynchoronous operation:
考虑使用异步操作的相同示例:
func testFunctionWithEscapingClosure(closure:@escaping () -> Void) {
DispatchQueue.main.async {
closure()
}
}
The above example contains an escaping closure because the closure invocation may happen after the function returns due to the asynchronous operation.
var completionHandlers: [() -> Void] = []
func testFunctionWithEscapingClosure(closure: @escaping () -> Void) {
completionHandlers.append(closure)
}
In the above case you can easily realize the closure is moving outside
body of the function so it needs to be an escaping closure.
Escaping and non escaping closure were added for compiler optimization in Swift 3. You can search for the advantages of nonescaping
closure.
在SWIFT 3中增加了转义闭包和非转义闭包用于编译器优化。您可以搜索非转义闭包的优点。
I find this website very helpful on that matter
Simple explanation would be:
我觉得这个网站对这件事很有帮助,简单的解释是:
If a closure is passed as an argument to a function and it is invoked
after the function returns, the closure is escaping.
Read more at the link I passed above! :)
点击上面的链接阅读更多内容!:)
By default the closures are non escaping. For simple understanding you can consider non_escaping closures as local closure(just like local variables) and escaping as global closure (just like global variables). It means once we come out from the method body the scope of the non_escaping closure is lost. But in the case of escaping closure, the memory retained the closure int the memory.
默认情况下,闭包是非转义的。为了简单理解,您可以将非转义闭包视为局部闭包(就像局部变量一样),将转义作为全局闭包(就像全局变量一样)。这意味着一旦我们离开了方法体,非转义闭包的作用域就丢失了。但在转义闭包的情况下,内存将闭包保留在内存中。
***Simply we use escaping closure when we call the closure inside any async task in the method, or method returns before calling the closure.
*当我们在方法中的任何异步任务内调用闭包时,或者在调用闭包之前方法返回时,我们只是使用转义闭包。
Non_escaping closure: -
非转义关闭(_E):-
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()
}
Since it is non_escaping closure its scope will be lost once we come out the from the 'add' method. completion(num1 + num2) will never call.
因为它是非转义闭包,所以一旦我们从‘Add’方法中出来,它的作用域就会丢失。完成(Num1+Num2)将永远不会调用。
Escaping closure:-
逃生关闭:-
func add(num1: Int, num2: Int, completion: @escaping((Int) -> (Void))) -> Int {
DispatchQueue.global(qos: .background).async {
print("Background")
completion(num1 + num2)
}
return num1
}
Even if the method return (i.e., we come out of the method scope) the closure will be called.enter code here
即使方法返回(即,我们超出了方法作用域),也会调用闭包。在此处输入代码
Swift 4.1
From Language Reference: Attributes of The Swift Programming Language (Swift 4.1)
Apple explains the attribute escaping
clearly.
苹果公司清楚地解释了转义属性。
Apply this attribute to a parameter’s type in a method or function declaration to indicate that the parameter’s value can be stored for later execution. This means that the value is allowed to outlive the lifetime of the call. Function type parameters with the escaping type attribute require explicit use of self. for properties or methods. For an example of how to use the escaping attribute, see Escaping Closures
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
The someFunctionWithEscapingClosure(_:)
function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping
, you would get a compile-time error.
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
Definition of Escape
逃逸的定义
Swift’s closures are reference types, which means if you point two variables at the same closure they share that closure – Swift just remembers that there are two things relying on it by incrementing its reference count.
SWIFT的闭包是引用类型,这意味着如果您将两个变量指向同一个闭包,它们共享该闭包--SWIFT只会通过递增其引用计数来记住有两件事依赖于它。
When a closure gets passed into a function to be used, Swift needs to know whether that function will get used immediately or whether it will be saved for use later on. If it’s used immediately, the compiler can skip adding one to its reference count because the closure will be run straight away then forgotten about. But if it’s used later – or even might be used later – Swift needs to add one to its reference count so that it won’t accidentally be destroyed.
当闭包传递到要使用的函数时,SWIFT需要知道该函数是否会立即使用,或者是否会保存以供以后使用。如果立即使用它,编译器可以跳过将1加到其引用计数中,因为闭包将立即运行,然后被遗忘。但如果它后来被使用-甚至可能被使用-SWIFT需要在它的引用计数中增加一个,这样它就不会意外地被销毁。
Quick Example
快速示例
A good example of an escaping closure is a completion handler. It’s executed in the future, when a lengthy task completes, so it outlives the function it was created in. Another example is asynchronous programming: a closure that’s executed asynchronously always escapes its original context.
一个很好的转义闭包的例子是完成处理程序。它会在将来执行,当一个冗长的任务完成时,所以它的寿命比创建它的函数长。另一个例子是异步编程:异步执行的闭包总是会转义其原始上下文。
public func responseData(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<Data>) -> Void)
-> Self
{
...
Extra Information
更多信息
For performance reasons, Swift assumes all closures are nonescaping closures, which means they will be used immediately inside the function and not stored, which in turn means Swift doesn’t touch the reference count. If this isn’t the case – if you take any measure to store the closure – then Swift will force you to mark it as @escaping so that the reference count must be changed.
出于性能原因,SWIFT假定所有闭包都是非转义闭包,这意味着它们将立即在函数中使用,而不是存储,这又意味着SWIFT不会触及引用计数。如果情况并非如此-如果您采取任何措施来存储闭包-则SWIFT将强制您将其标记为@ESCRING,因此必须更改引用计数。
non-escaping(@noescape) vs escaping(@escaping) closure
非转义(@noscape)与转义(@ESCRING)闭包
[Function and closure]
[函数和闭包]
non-escaping closure
不转义闭合
@noescape
is a closure which is passed into a function and which is called before the function returns
@noscape是传递到函数中并在函数返回之前调用的闭包
A good example of non-escaping closure
is Array
sort function
- sorted(by: (Element, Element) -> Bool)
. This closure is called during executing a sort calculations.
非转义闭包的一个很好的例子是数组排序函数-sorted(by:(Element,Element)->Bool)。此闭包在执行排序计算期间调用。
History: @noescape
was introduced in Swift 2
-> was deprecated in Swift 3
where became a default that is why you should mark @escaping
attribute explicitly.
历史:在SWIFT 2中引入了@NOESCRAPE->在SWIFT 3中被弃用,成为默认设置,这就是为什么您应该显式标记@ESCRING属性。
func foo(_ nonEscapingClosure: () -> Void) {
nonEscapingClosure()
}
escaping closure
转义闭合
escaping closure(reference) is alive when method was finished.
当方法完成时,转义闭包(引用)是活动的。
//If you have next error
Escaping closure captures non-escaping parameter
//`@escaping` to the rescue
@escaping
is a closure which is
@转义是一个结束,这是
- passed into a function
- The owner function saves this closure in a property
- closure is called(using property) after the owner function returns(async)
A good example of an escaping closure
is a completion handler
in asynchronous operation. If you do not mark you function with @escaping
in this case you get compile time error. Reference to property in an escaping closure
requires you to use self
explicitly
转义闭包的一个很好的例子是异步操作中的完成处理程序。在这种情况下,如果您没有用@ESCRING标记您的函数,您就会得到编译时错误。对转义闭包中的属性的引用要求您显式使用self
class MyClass {
var completionHandler: (() -> Void)?
func foo(_ escapingClosure: @escaping () -> Void) {
//if you don't mark as @escaping you get
//Assigning non-escaping parameter 'escapingClosure' to an @escaping closure
completionHandler = escapingClosure //<- error here
}
func onEvent() {
completionHandler?()
}
}
completion
is a completionHandler
About, if you want to highlight that it is @escaping you can use completionHandler
naming
Complete是一个CompletionHandlerAbout,如果您想突出显示它是@Esscing,您可以使用CompletionHandler命名
func foo(completion: () -> Void) {
//
}
func foo(completionHandler: @escaping () -> Void) {
//
}
[Sync vs Async]
[Java functional interfaces]
[同步与异步][Java功能接口]
Here simple example of escaping and no escaping closures.
Non-Scaping Closures
这里是转义闭包和不转义闭包的简单示例。非美化封闭式
class NonEscapingClosure {
func performAddition() {
print("Process3")
addition(10, 10) { result in
print("Process4")
print(result)
}
print("Process5")
}
func addition(_ a: Int, _ b: Int, completion: (_ result: Int) -> Void) {
print("Process1")
completion(a + b)
print("Process2")
}}
Creating an instance and calling function calling
创建实例并调用函数调用
let instance = NonEscapingClosure()
instance.performAddition()
Output:
产出:
Process3
Process1
Process4
20
Process2
Process5
And Escaping Closures
和转义闭包
class EscapingClosure {
func performAddition() {
print("Process4")
addition(10, 10) { result in
print(result)
}
print("Process5")
}
func addition(_ a: Int, _ b: Int, completion: @escaping (_ result: Int) -> Void) {
print("Process1")
let add = a + b
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("Process2")
completion(add)
}
print("Process3")
}}
Creating an instance and function calling
创建实例和函数调用
let instance = EscapingClosure()
instance.performAddition()
Output
输出
Process4
Process1
Process3
Process5
Process2
20
An escaping closure is one that we want to run at a later point in time. By default all closures we define in Swift are executed immediately. Meaning as soon as they are encountered at runtime, then are executed and done.
转义闭包是我们希望在稍后的时间点运行的闭包。默认情况下,我们在SWIFT中定义的所有关闭都会立即执行。这意味着一旦在运行时遇到它们,就会被执行和完成。
Escaping closures are ones that get stored and are executed at some point in the future - like when a network call completes.
转义闭包是指被存储并在将来某个时候执行的闭包--比如当网络调用完成时。
So think of regular closures as closures that get executed immediately. And escaping closures as ones that get stored and are executed at some future point in time.
因此,可以将常规闭包视为立即执行的闭包。将闭包转义为存储并在将来某个时间点执行的闭包。
更多回答
@bandejapaisa You say "@noescape" has been deprecated, but I have recently run into a situation that makes me think it is only semi-deprecated. Please take a look at this and let me know if I've made some mistake: stackoverflow.com/questions/41917413/…
@bandejapaisa你说@noscape已经被弃用了,但我最近遇到了一种情况,让我认为它只是半弃用的。请看一下这个,如果我犯了什么错误,请告诉我:Stackoverflow.com/Questions/41917413/…
@noescape is the implicit behaviour. The keyword is deprecated, not completely removed yet, but the behaviour you get by writing it or not, is that your closure does NOT escape from the scope of the method. Deprecated means it's still available to use, but it will be removed in a future version. Hence why you can still use it, and why you think it's 'semi deprecated'
@noescape是隐式行为。这个关键字已经被弃用了,还没有完全删除,但是不管你写不写,你得到的行为是你的闭包不会从方法的作用域中逃逸。弃用意味着它仍然可以使用,但它将在未来的版本中被删除。因此,为什么你仍然可以使用它,为什么你认为它是'半弃用'
From your answer, I still didn't understand the difference - what is each type good for. It would be nicer if you provided a real-world example. The other two answers are much easier to understand.
从你的回答中,我仍然不明白其中的区别--每种类型的产品都有什么好处。如果你能提供一个真实的例子,那就更好了。另外两个答案要容易理解得多。
@HonzaKalfus Sometimes closures escape and sometimes they don't. It all depends on what the method accepting the closure is doing. Escaping closures might cause retain cycles, non-escaping ones usually don't. @escaping
is a sign that tells the caller "beware, this closure will escape. make sure to capture self weakly or unowned".
@HonzaKalfus有时闭包转义,有时不转义。这完全取决于接受闭包的方法正在做什么。转义闭包可能会导致保留循环,而非转义闭包通常不会。@转义是一个标志,告诉调用者“当心,这个闭包将转义。确保捕获弱的或无主的自身”。
@Sweeper In The Swift Programming Language book, the authors claim this: "Swift uses capture lists to break these strong reference cycles." I haven't read the dedicated chapter on this issue though. But I'm unsure if that's even related to what you're saying :) Anyway, that wasn't the point of my comment. The point of my comment was that you could perhaps think about updating your answer by using a real-world example. Also, Swift 3 version would be nice, rather than 2, which is no longer used ;)
@Sweeper在《SWIFT编程语言》一书中,两位作者声称:“SWIFT使用捕获列表来打破这些强大的引用循环。”不过,我还没有读过关于这个问题的专门章节。但我不确定这是否与你所说的有关:)不管怎样,这不是我评论的重点。我的评论的要点是,也许你可以考虑用一个真实的例子来更新你的答案。此外,SWIFT的3版会更好,而不是现在不再使用的2版;)
It would be good to also add to this good answer what advantages of the nonescaping closure there are and examples when you can need it.
在这个好的答案中加上不可转义闭包有什么好处,以及当您需要它的时候有什么例子,这将是很好的。
我是一名优秀的程序员,十分优秀!