Swift Quickie: Capture Lists
Closures for fun and profit
This is the first in a series of short posts addressing easy things you might not know about. I'm going to try interleaving these thing the long-form major effort-posts.
Capture Lists
Many people don't know that you can explicitly specify capture lists for closures in Swift.
A closure closes over values from the local environment, creating a new execution environment for the code inside the closure. Objective-C blocks or C# lambdas are also examples of closures. I recommend Structure and Interpretation of Computer Programs and this dated but awesome video playlist if you want to really understand closures, execution environments, binding of names, and more.
Most often this is used to modify the storage of the item you are capturing, i.e. by making self
unowned or weak to break a retain cycle:
{ [unowned self] in
...
}
The Language Guide claims you should use unowned
if the closure and containing object reference each other and will be destroyed at the same time. Presumably that's to avoid the cost of safely nil'ing out a weak
reference in an object that's about to dealloc anyway.
Fun
Most people aren't aware that you can create new named values here as well:
{ [thing1 = self.grabThing(), weak thing2 = self.something] in
...
}
This avoids polluting the surrounding code with a reference to thing1
and thing2
. For bonus points, thing2
is also a weak reference.
Capture lists require the trailing in
keyword and come before the argument list if you have one:
{ [unowned self] (a:A, b:B) -> ReturnType in
...
}
Meta
That's it for this quick post. If you have any suggestions or corrections please let me know.
This blog represents my own personal opinion and is not endorsed by my employer.