Why doesn't quick closure take hold?

I tested the quick close of the Xcode playground.

This is my code:

import UIKit class A{ var closure: ()->() = {} var name: String = "A" init() { self.closure = { self.name = self.name + " Plus" } } deinit { print(name + " is deinit") } } var a: A? a = A() a = nil 

As expected, a is closed closed, so a is never released.

But, when I add this line to the last line:

 a?.closure = { a?.name = "ttt" } 

Then I found that "A is deinit" in the output window, which means that a is freed. What for? is not a recycle reference?

To be a test, I use a function to set a closure whose code is version 2:

 import UIKit class A{ var closure: ()->() = {} func funcToSetClosure(){ self.closure = { self.name = "BBB"} } var name: String = "A" init() { self.closure = { self.name = self.name + " Plus" } } deinit { print(name + " is deinit") } } var a: A? a = A() a?.funcToSetClosure() a = nil 

Again, a is never released.

So, I came to the conclusion that when the closure is set by init or by a function in the class, it will cause a recycle reference, when it is inserted towards the class, it will not cause a recycle reference. I'm right?

+6
source share
3 answers

In both cases, cycles are saved. The difference is in the nature of the link, not in the place where closure set. This difference is manifested in what is needed to break the cycle:

  • In the β€œinside” situation, the link inside the self closure. When you release the link to a , this is not enough to break the loop, because the loop is directly self-referential. To break the loop, you would also have to set a.closure to nil before setting a to nil , and you did not.

enter image description here

  • In the "external" situation, reference a . There is a save loop if link a not set to nil . But you end up setting it to nil , which is enough to break the loop.

enter image description here

(The illustrations are taken from the graph of the Xcode memory graph. So great.)

+4
source

What causes the save loop is that you are referencing self in closure.

 var a: A? a = A() a?.closure = { a?.name = "ttt" } a = nil 

You change the closure so that you no longer refer to self , so it is freed.

In the last example, you again refer to self in closure, so it is not freed. There are ways around this, this post is a great list of when to use each case in the fast: How to handle the Weak Self in Quick Blocks with Arguments

I would suggest that you are looking for something like this, where you use a weak reference to yourself inside the block. Swift has several new ways to do this, most often using the [unowned self] notation at the front of the block.

 init() { self.closure = { [unowned self] in self.name = self.name + " Plus" } } 

More on what's happening here: Should we always use [unowned self] inside closure in Swift

+2
source

According to the SIL documentation , when capturing a local variable in closure, it will be stored on the heap with reference counting:

Captured local variables and useful values ​​of indirect value types are stored in the heap. The @box T type is a reference count that refers to a field containing a mutable value of type T

Therefore, when you say:

 var a : A? = A() a?.closure = { a?.name = "ttt" } 

you have a reference loop (which you can easily check). Is this because instance A has a reference to the closure property, which has a reference to instance A? highlighted in the cube A? in the box (due to the fact that it is captured by the closure), which in turn has a link to instance A

However, then you say:

 a = nil 

Which sets the value of instance A? placed in a heap in A? , thus freeing its reference to instance A , so that means you no longer have a reference loop, and thus A can be redistributed.

Just removing A goes beyond the scope without assigning a = nil will not break the reference cycle, since instance A? the heap is still stored by the closure A property, what is still stored by the instance of A? .

+2
source

Source: https://habr.com/ru/post/1012908/


All Articles