After reading some articles and developer guide of apple, i'm still confused about Capture List in closure. What does it mean "capture", how does it work behind the scene in terms of unowned self and weak self? how the closure use self without owning the object? I thought it like making a copy of that object so when it get finished it's passed from the stack like value type, but i guess that i'm wrong. I'm hoping that someone here can make it more easier and clear for understanding, or linked me to a good article that answering this specific question. Thanks for advance
Swift - Capture List self clarification
662 views Asked by r.pul AtThere are 3 answers
My understanding, and it might be a bit simplified, is that it is about ownership and holding on to an object, meaning that as long as we claim ownership of an object it can not be freed from memory even another part of the code sets it to nil or similar.
With weak
we say that it is okay to destroy the object and that we will only use it if it is still around.
So when declaring self
as weak
in a closure we say that if self
is still around when it's time to execute the closure we do so normally otherwise the closure will silently be ignored without generating an error.
It's mainly to do with reference counting. Any instance that is used inside a closure (but was declared outside) is strongly referenced (i.e. its reference count is incremented). This can lead to retain cycles, e.g.
class MyClass {
var myClosure: (() -> Void)!
init() {
myClosure = {
self.foo()
}
}
func foo() {
}
}
In the above example the instance of MyClass
retains a reference to myClosure
and vice versa, meaning that the MyClass
instance will stay in memory forever.
You can also have more complex/harder-to-spot retain cycles, so you need to really pay attention and if you ever have any doubts add some print
calls to your class' deinit
methods just to make sure (or use Instruments).
In order to avoid these issues you can mark objects being captured in closures as unowned
or weak
. This means that their reference count won't be increased and you can avoid these retain cycles. The above example could have either been done this way:
myClosure = { [weak self] in
self?.foo()
}
or, better yet for this example, this way:
myClosure = { [unowned self] in
self.foo()
}
While the first way is always safe and what you will more likely do, the unowned
version is easy to reason in this example because you know that myClosure
won't outlive self
. However, if you're not 100% sure that self
will always outlive the closure use weak
.
Also note that you can mark how to capture multiple objects used inside the closure, just separate it by commas, e.g.
myClosure = { [weak self, unowned bar] in
self?.foo(bar)
}
If we keep in mind that captured values are strong references in closures by default, we can assume that this can create retain cycles (bad stuff).
A capture list is an array of variables you can pass into a closure. The purpose of capture lists is to change the strenght of the variables that are passed in. This is used to break retain cycles.
For instance: