快速@转义和完成处理程序

我试图更准确地理解斯威夫特的“终结”。

但是 @escapingCompletion Handler太难理解了

我搜索了很多斯威夫特的帖子和官方文件,但我觉得还是不够。

这是官方文档的代码示例

var completionHandlers: [()->Void] = []


func someFunctionWithEscapingClosure(completionHandler: @escaping ()->Void){
completionHandlers.append(completionHandler)
}


func someFunctionWithNoneescapingClosure(closure: ()->Void){
closure()
}


class SomeClass{
var x:Int = 10
func doSomething(){
someFunctionWithEscapingClosure {
self.x = 100
//not excute yet
}
someFunctionWithNoneescapingClosure {
x = 200
}
}
}


let instance = SomeClass()
instance.doSomething()
print(instance.x)


completionHandlers.first?()
print(instance.x)

我听说使用 @escaping有两种方式和原因

第一个用于存储闭包,第二个用于异步操作。

以下是我的问题:

首先,如果执行 doSomething,那么 someFunctionWithEscapingClosure将使用闭包参数执行,并且该闭包将保存在全局变量数组中。

我认为闭包是{ self. x = 100}

保存在全局变量 completionHandlers中的{ self. x = 100}中的 self如何连接到 SomeClass的对象 instance

第二,我是这样理解 someFunctionWithEscapingClosure的。

将局部变量闭包 completionHandler存储为全局变量‘ CompletionHandlerswe using@escape’关键字!

如果没有 @escaping关键字 someFunctionWithEscapingClosure返回,局部变量 completionHandler将从内存中删除

@escaping是将这个闭包保留在内存中

是这样吗?

最后,我想知道这种语法是否存在。

也许这是一个非常基本的问题。

如果我们想要某个函数在某个特定函数之后执行。我们为什么不在特定的函数调用之后调用某个函数呢?

使用上面的模式和使用转义回调函数有什么区别?

77058 次浏览

Swift Completion Handler Escaping & Non-Escaping:

Assume the user is updating an app while using it. You definitely want to notify the user when it is done. You possibly want to pop up a box that says, “Congratulations, now, you may fully enjoy!”

So, how do you run a block of code only after the download has been completed? Further, how do you animate certain objects only after a view controller has been moved to the next? Well, we are going to find out how to design one like a boss.

Based on my expansive vocabulary list, completion handlers stand for

Do stuff when things have been done

Bob’s post provides clarity about completion handlers (from a developer point of view it exactly defines what we need to understand).

@escaping closures:

When one passes a closure in function arguments, using it after the function’s body gets executed and returns the compiler back. When the function ends, the scope of the passed closure exist and have existence in memory, till the closure gets executed.

There are several ways to escaping the closure in containing function:

  • Storage: When you need to store the closure in the global variable, property or any other storage that exist in the memory past of the calling function get executed and return the compiler back.

  • Asynchronous execution: When you are executing the closure asynchronously on despatch queue, the queue will hold the closure in memory for you, can be used in future. In this case you have no idea when the closure will get executed.

When you try to use the closure in these scenarios the Swift compiler will show the error:

error screenshot

For more clarity about this topic you can check out this post on Medium.

Adding one more points , which every ios developer needs to understand :

  1. Escaping Closure : An escaping closure is a closure that’s called after the function it was passed to returns. In other words, it outlives the function it was passed to.
  2. Non-escaping closure : A closure that’s called within the function it was passed into, i.e. before it returns.

Here's a small class of examples I use to remind myself how @escaping works.

class EscapingExamples: NSObject {


var closure: (() -> Void)?


func storageExample(with completion: (() -> Void)) {
//This will produce a compile-time error because `closure` is outside the scope of this
//function - it's a class-instance level variable - and so it could be called by any other method at
//any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
//function.
closure = completion
//Run some function that may call `closure` at some point, but not necessary for the error to show up.
//runOperation()
}


func asyncExample(with completion: (() -> Void)) {
//This will produce a compile-time error because the completion closure may be called at any time
//due to the async nature of the call which precedes/encloses it.  We need to tell `completion` that it should
//stay in memory, i.e.`escape` the scope of this function.
DispatchQueue.global().async {
completion()
}
}


func asyncExample2(with completion: (() -> Void)) {
//The same as the above method - the compiler sees the `@escaping` nature of the
//closure required by `runAsyncTask()` and tells us we need to allow our own completion
//closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until
//it is executed, so our completion closure must explicitly do the same.
runAsyncTask {
completion()
}
}










func runAsyncTask(completion: @escaping (() -> Void)) {
DispatchQueue.global().async {
completion()
}
}


}
/*the long story short is that @escaping means that don't terminate the function life time until the @escaping closure has finished execution in the opposite of nonEscaping closure the function can be terminated before the closure finishes execution Ex:
*/


func fillData(completion: @escaping: () -> Void){
/// toDo
completion()
}


//___________________________


//The call for this function can be in either way's @escaping or nonEscaping :


    

fillData{
/// toDo
}
    



/* again the deference between the two is that the function can be terminated before finish of execution nonEscaping closure in the other hand the @escaping closure guarantees that the function execution will not be terminated before the end of @escaping closure execution. Hope that helps ***#(NOTE THAT THE CLOSURE CAN BE OF ANY SWIFT DATA TYPE EVEN IT CAN BE TYPEALIAS)*/