In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly shows you "how to achieve escape closures in Swift". The content is simple and clear. I hope it can help you solve your doubts. Let me lead you to study and learn "how to achieve escape closures in Swift".
Escape and non-escape closure
When talking about escape closures, we always refer to closures provided as function or method parameters. In general, we divide the closures provided to methods (or functions) into two categories:
The closure that is called before the method execution is complete.
The closure that is called after the method execution is complete.
In the latter case, we are talking about escape closures; turn off the xecution method that continues even after electronic residence until we call them at any time in the future.
In the former case, contrary to what I described above, we call the closure non-escaping.
Until Swift 3, by default, all closures passed as arguments to a method or function are considered escaped. Since Swift 3, this is no longer true; by default, all methods are considered non-escaped, which means they are called before method execution is complete.
The following code section demonstrates a non-escaped closure:
Func add (num1: Double, num2: Double, completion: (_ result: Double)-> Void) {let sum = num1 + num2 completion (sum)}
The completion closure executes the method called by the code leaf before, so this is not an escape closure case.
However, how does a closure escape from a method, so we end up with the opposite result?
Escape method
In order to make a closure an escaped closure, it is necessary to keep a reference to it outside the scope of the method so that we can use it later. Look at the following code:
Class Demo {var result: Double? Var resultHandler: (()-> Void)? Func add2 (num1: Double, num2: Double, completion: ()-> Void) {resultHandler = completion result = num1 + num2}}
Here we have a result property that holds the result of the addition that occurs inside the method. But we also have property in resultHandler; this maintains a reference to the closure provided by completion as a method parameter.
The closure is escaped from the method by the following line:
ResultHandler = completion
However, this is not the only operation required, so we can say that the completion is an escaped closure. We must specify the compiler, or we will see the following error in Xcode:
To fix it, we need to mark the closure with the @ escaping attribute. We put this attribute after the closure name and semicolon, but before the closure type, as follows:
Func add2 (num1: Double, num2: Double, completion: @ escaping ()-> Void) {.}
The compiler no longer complains, and completion is now officially an escaped closure.
Put the escape closure into action
Let's add two more methods to the Demo class above; one will call the add2 (num1:num2:completion:) method, and the other will call the resultHandler closure to get the final result:
Class Demo {... Func doubleSum (num1: Double, num2: Double) {add2 (num1: num1, num2: num2) {guard let result = self.result else {return} self.result = result * 2}} func getResult () {resultHandler?
The first method doubles the results calculated by the add method. However, the result does not double, and the code inside the closure body of the method is not executed until add2 (num1:num2:completion:) we call the getResult () method.
This is where we benefit from escaping closures; we can trigger calls to closures when needed in our code and at the right time. Although the examples provided are deliberately too simple, in real-world projects, the practical advantages become more obvious and bold.
Pay attention to strong reference period
Let's add the last one to the Demo class and implement the default initializer and destructor methods:
Class Demo {init () {print ("Init")} deinit {print ("Deinit")}...}
Init () is the first method called when Demo initializes the instance, and deinit is the last method called before releasing the instance. I added a print command to all of them to verify that they were called and that there was no memory leak when using the method with the above escaped closure.
Note: when we try to free an object by setting it to nil, there may be a memory leak, but the object remains in memory because of strong references between the object and other objects that keep it active.
Now, let's add the following lines to use all of the above:
Var demo: Demo? = Demo () demo?.doubleSum (num1: 5, num2: 10) demo?.getResult () print ((demo?.result!)!) demo = nil
First, we initialize an optional instance of the class, Demo, so that we can set it to nil later. Then, we call the doubleSum (num1:num2:) method to add the two numbers given as parameters, and then double the result. However, as I said before, this will not happen until we call the getResult () method; the add2 (num1:num2:completion:) that escaped the closure is actually called in the method.
Finally, we print the value demo of the result property in the instance and set its demo to nil.
* Note: * as shown in the code snippet above, use the exclamation point (!) It is a very bad practice to force the expansion of optional values, please do not do so. The only reason I'm doing this here is to make things as simple as possible.
The above line prints the following:
Init
30.0
Note that the "Deinit" message is missing here! In other words, deinit does not call this method, which proves that there is no actual result for making demo instance nil. It seems that with just a few simple lines of code, we managed to solve the memory leak problem.
The reason behind the memory leak
Before we can find a solution to the memory leak, it is necessary to understand why it happened. In order to find it, let's take a few steps back to modify what we did earlier.
First, we escape from the method using the following completion exercise closure:
ResultHandler = completion
This line is more "guilty" than it looks because it creates a strong reference to the closure completion.
Note: closures are reference types, just like classes.
However, this alone is not enough to cause a problem, because releasing an instance of demo removes the reference to the closure. The real trouble begins with the closure body inside the doubleSum (num1:num2:) method.
There, this time we create another strong reference from the closure to the demo instance by capturing the * * object when using object access properties: selfresult
Guard let result = self.result else {return} self.result = result * 2
When they are all in place, the Demo instance maintains a strong reference to the closure, which is a strong reference to the instance. This creates a reserved loop, also known as a strong reference loop. When this happens, each reference type keeps the other reference type active in memory, so they are not eventually released.
Note that this only happens in classes that contain methods with escaped closures. The case of structs is different because they are not references but value types, and explicit references to self are not mandatory.
Eliminate strong reference loops
There are two ways to avoid strong reference loops, thereby avoiding memory leaks. The first is to manually and explicitly release the reference to the closure after we call the closure:
Func getResult () {resultHandler? / / Setting nil to resultHandler removes the reference to closure. ResultHandler = nil}
The second method is to weakly * * capture the self instance in the body of the closure:
Func doubleSum (num1: Double, num2: Double) {add2 (num1: num1, num2: num2) {[weak self] in guard let result = self?.result else {return} self?.result = result * 2}}
[weak self] in sees the addition after closing and opening. With this, we established a weak reference to the Demo instance, so we avoided the retention loop. Note that we use self as an optional value, followed by a question mark (?) Symbols.
There is no need to apply these two changes to avoid strong reference loops. No matter which one we finally choose, from now on, the output will also contain a "Deinit" message. This means that the demo object becomes nil, and we no longer have memory leaks.
Init
30.0
Deinit
Generalization
One thing to take with you to get out of here is to be careful when using escape closures. Whether you are implementing your own method of accepting escaped closures as parameters, or using API with escaped closures, always make sure that you do not end with a strong reference loop. Memory leaks are a big "taboo" when developing applications, and we certainly don't want our applications to crash or be terminated by the system at some point. On the other hand, don't hesitate to use escape closures; they provide the advantage of generating more powerful code.
These are all the contents of the article "how to achieve escape closures in Swift". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.