“This is the 26th day of my participation in the First Challenge 2022. For details: First Challenge 2022”
We talked about mutual references between two class instances in weak and unreferenced references. It is also possible to have mutual references between classes and attributes in a class whose attributes are closures.
A closure is a special syntactic construct in which instances of reference types used increase the reference count by one. Therefore, if you use the self keyword in a closure attribute, you add one to the reference count of the current class instance itself. Because the closure is an attribute of the current class, the closure attribute cannot be destroyed, and the current class instance cannot be destroyed either. Conversely, the current class instance cannot be destroyed, nor can the closure properties. This creates a circular reference, which creates a memory leak.
Use of closures
Let’s start with a simple piece of code:
We changed num inside the closure, and the final print shows that the external num value has changed as well;
Changes to variables inside closures change the values of the original variables outside;
So if we capture a class’s attributes in a closure, does that make the class undestroyable?
As shown in the code above, we captured the age property in the closure, but p can still be destroyed by normal release;
Closure properties
Next, we define a closure property in Person as follows:
We’ve defined a closure in Person that uses self.age in the closure, and it can’t be destroyed even if we set p to nil; That is, the reference count of P is not zero; So how to solve it?
To deal with circular references in this case, Swift provides a capture list for closure structures that can be used to convert weak or unreferenced references to variables or instances used within the closure. The capture list needs to be structurally immediately after the opening brace of the closure, surrounded by the middle brace, and we modify the code as follows:
After the [weak self] is converted to a weak reference, the final instance is freed; Of course, the same effect can be achieved with unowned:
Capture the list
So what is a capture list? Let’s analyze it through the following code:
We define two variables width and height, and we define a capture list of width in the closure. After we change width and height externally, when we call the closure, we find that only height is changed, but width is not changed. So what exactly does the capture list do to the width variable?
After we declared the width of the capture list, so in the process of the program runs, the compiler will be in the context of the program is run by the search with the same name of a variable or constant, find, will use the current variable literal value to initialize the age capture (age) on the list, so we use in closure width, He’s still a 10; The inside of the closure remains the same no matter how much width is changed externally. The value of the inside of the closure depends on the literal value of the variable with the same name as width in the capture list.
We can treat width in the [width] capture list as a constant. The value of this constant is the value of the variable or literal of the constant with the same name; The width in the capture list and the external variable width are two very different things; The timing of capture occurs when the closure executes;
If you need to convert values of multiple reference types in a capture list, separate them with commas.
The little knowledge
Inside a closure, you can use the withExtendedLifetime function to extend the lifetime of an optional value:
Note that the extended range is within the withExtendedLifetime closure expression;