class Name {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) deinit")
}
}
var x: Name? = Name(name: "abc")
var someClosure = {
print("\(x?.name)")
}
someClosure()
x = nil
And then the console will output:
Optional("abc")
abc deinit
It can be seen that the "deinit" function was called. So it does not form a strong reference cycle. But if I add a capture list to the closure:
var someClosure = { [x] in
print("\(x?.name)")
}
The console will output:
Optional("abc")
And the "deinit" function was not called. So the object and reference form a strong reference cycle.
What is the reason? What's the difference between these two conditions?
First of all, there's no strong retain cycle in either case – you just simply have a global closure variable that's holding a strong reference to your class instance, therefore preventing it from being deallocated.
In your first example, when you capture
xin the closure:What you've got (in effect) is a reference to a reference – that is, the closure has a reference to the storage of
x, which then has a reference to your class instance. When you setxtonil– the closure still has a reference to the storage ofx, but nowxdoesn't have a reference to your class instance. Thus, your class instance no longer has any strong references to it, and can be deallocated.In your second example when you use a capture list:
You're copying
xitself – that is, you're copying a reference to the instance of your class. Therefore the closure will keep your class retained as long as it exists. Settingxtonildoesn't affect the closure's reference to your instance, as it has it's own strong reference to it.