Introduction
In this article, we’ll see mainly two things: how a closure captures a variable and a specific scenario where the closure captures the self
context.
You don’t have to be a Guru of Swift closures to understand this article, but I suggest you a good understanding of what’s a strong and weak reference before starting reading this article, otherwise something may be confusing since I don’t explain thoroughly how an object reference works in Swift.
Happy Reading!
Default Variable Capturing
When we use a closure, most of the time, we need to use variables from the surrounding context—like class properties or local variables. We are able to use these variables capturing them inside the closure. The capturing is transparent to us, we can use these variables without any efforts like in the following example:
struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
let calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
closure() // Prints "The result is 8"
In this example, there is a struct Calculator
which provides the sum of two properties. Then, we have a Calculator
instance—calculator
— which is captured inside closure
to print sum
.
Note
When we capture a variable and change its value inside the closure, we affect its value also outside the closure scope once the closure is called:
var calculator = Calculator(a: 3, b: 5) // 0x618000220400
let closure = {
calculator = Calculator(a: 33, b: 55) // 0x610000221be0
}
// calculator has address 0x618000220400
closure()
// calculator has address 0x610000221be0
In the example above, we instantiate a Calculator
object which has the memory address 0x618000220400
. Then, inside the closure, we assign a new value to calculator
and the address becomes 0x610000221be0
. Finally, after calling the closure, calculator
remains the one set inside the closure, as we can see with the memory address.
Capture List
Unfortunately, the implementation used in “Default Variable Capture” has a problem. If we change the value of some calculator
properties before calling the closure, the sum inside the closure is no longer 8
but it’s the sum of the new properties values:
var calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
calculator.b = 20
closure() // Prints "The result is 23"
If we want to prevent this behaviour and print 8
even if the properties change after their capturing inside the closure, we can explicitly capture the variable with a capture list like this:
let closure = { [calculator] in
print("The result is \(calculator.sum)")
}
In this way, we keep an immutable copy of the variable calculator
. Thanks to this copy, further changes to calculator
, outside the closure, will not affected the closure.
We can capture several variables in the same closure, separating them with a comma:
let closure = { [variable1, variable2, variable3] in
print(variable1)
print(variable2)
print(variable3)
}
Note
Since the variables inside the capture list are immutable, they are just read-only. It means that we cannot change the value of calculator
inside the closure:
let closure = { [calculator] in
calculator = Calculator(a: 1, b: 2) // Throws compile error
print("The result is \(calculator.sum)")
}
Make Aliases
When we use a capture list, we can also use aliases to use a different name for a specific variable with the syntax alias = variable_name
:
let closure = { [alias1 = variable1, variable2, alias3 = variable3] in
print(alias1)
print(variable2)
print(alias3)
}
Therefore, we can change the closure used in the previous examples like this:
let closure = { [calc = calculator] in
print("The result is \(calc.sum)")
}
Capture Self Context
Explicit self
If we want to capture some properties or methods of the object where we set the closure, like in the following example:
class MyClass {
var p = 0
func method() {
let closure = {
print("P is \(p)")
}
closure()
}
}
we have the following compile error: Reference to property 'p' in closure requires explicit 'self.' to make capture semantics explicit
.
This means that, when we want to capture some properties or methods in the self
context, we must add explicitly self
to tell the compiler that the variable comes from the object self
—MyClass
in our example.
We can fix the compile error like this:
class MyClass {
var p = 0
func method() {
let closure = {
print("P is \(self.p)")
}
closure()
}
}
Strong Reference Cycles
A strong reference cycle occurs when two objects keep a strong reference of each other. Because of this cycle, the two objects won’t be deallocated, since their reference count doesn’t drop to zero.
Let’s see an example:
class ClassA {
var refB: ClassB?
}
class ClassB {
var refA: ClassA?
}
let classA = ClassA()
let classB = ClassB()
classA.refB = classB
classB.refA = classA
This is a very plain example where we can see that both classA
and classB
are keeping a strong reference of each other creating a strong reference cycle.
This is just a plan example and the cycle is very easy to catch. It may become more difficult when we capture self
inside a closure, if we don’t understand well how the closure works.
By default, a closure keeps a strong reference of the variable captured. This means that in the following example:
class MyClass {
func doSomethig() { }
}
class Handler {
var closure: (() -> Void)?
func setupClosure() {
let obj = MyClass()
closure = {
obj.doSomethig()
}
}
}
let handler = Handler()
handler.setupClosure()
obj
won’t be deallocated at the end of setupClosure
scope since closure
is keeping a strong reference of it. It will be deallocated once we destroy closure
.
It means that if we capture self
inside a closure—for example to use a property or a method—we keep a strong reference of self
.
Let’s see an example where we use self
inside a closure creating a strong reference cycle:
class MyClass {
func doSomethig() { }
}
class Handler {
var closure: (() -> Void)?
let obj = MyClass()
func setupClosure() {
closure = {
self.obj.doSomethig()
}
}
}
let handler = Handler()
handler.setupClosure()
In this example, handler
keeps a strong reference of its properties closure
and obj
. Then, in setupClosure
, closure
keeps an additional strong reference of self
to use its obj
property. In this way, we are creating a strong reference cycle, since closure
and self
are keeping a strong reference of each other.
We can break this strong reference cycle using self
in the capture list with either a weak or an unowned reference:
Weak
A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. This behaviors prevents the reference from becoming part of a strong reference cycle.
Since a weak reference may be nil
, the variable captured becomes an optional. Therefore, we should use a guard
to safety unwrapped it:
closure = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.obj.doSomethig()
}
Unowned
Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime.
This means that we should use unowned
just when we are sure that the reference will never be nil
inside the closure, otherwise the app would crash.
We can use an unowned
reference like this:
closure = { [unowned self] in
self.obj.doSomethig()
}
We don’t need the guard
like for weak
since an unowned reference is not optional since it cannot be nil.
Note
- We can use
weak
andunowned
with any variable in the capture list and we can also combine with the aliases:let obj = MyClass() let closure = { [unowned aliasObj = obj] in aliasObj.doSomethig() }
- We don’t need to use
weak
/unowned
every time we useself
. There are situations where we don’t need it—like with theUIView
animation method:UIView.animate(withDuration: 5, animations: { self.obj.doSomethig() })
Since we are using a static
UIView
method andself
doesn’t keep any strong reference ofUIView
.
Conclusion
The closure is a very powerful Swift feature which allows us to read a clean and readable Swifty codebase. Moreover, we have to use closures in a lot of Apple API—like in Foundation
, UIKit
and so on. To take advantage of closures we must be able to master it before to hurt ourself. For this reason, with a good understanding of how a closure captures the variables, we may be able to avoid troubles like strong reference cycles.
29/06/2018 at 17:57
Thank you so much for this!
25/07/2017 at 21:04
Ok Marco, thank you for your further explanation!
26/07/2017 at 11:43
You’re welcome, Luca! Feel free to ask other questions for any doubt.
24/07/2017 at 20:12
Hi Marco, thank you for your reply.
The second part of your answer goes to the point.
Could you please point me to an example of a closure factory class that can go out of scope and be deinit while its closure still has some kind of usefulness? The closure can only perform computations and return results that are not based on any kind of surrounding environment.
I totally agree with you that when self is supposed to be not nil, than [unowned self] makes this assumption explicit.
What I am asking is: isn’t [unowned self] the way to go, most of the times?
25/07/2017 at 09:46
If we use the Alamofire example, if we use it inside the class
HTTPHandler
, we send a new network request and in the meanwhileHTTPHandler
has been destroyed, we have still the network request alive until we receive a response.In my personal opinion, we should use
unowned
as much as possible. Unfortunately, we abuse ofweak
because it’s safe all the times, but if we know what we’re doing we should useunowned
every time we’re sure that self will never be nil.23/07/2017 at 22:20
Hi Marco, thank you for your article.
I’m on my first steps with Swift and I can’t figure out when it is legitimate to expect that the “closure factory” can go out of scope and be deinitialized while someone else still has a good reason to use the closure (someone that retains a reference to the closure).
In this situation I think that self should be nulled and that the guard statement should execute the else branch, am I right?
What I can’t understand is if there is a good reason to expect that this situation isn’t a silenced bug in my code.
Wouldn’t be better in this situation to log an error message and just fail instead of returning some default return value?
Thank you again, Luca-
24/07/2017 at 12:47
Hi Luca,
I hope I got your comment. I understand that you have mainly two doubts:
When a closure can escape the scope
You have this scenario with an async completion closure. I’m pretty sure you know Alamofire, it has an escaping closure for the completion of its request:
Here we must have an escaping closure because we’re using a network request and it’s not sync, the completion closure is called just once the HTTP request is completed. (In this example I’m using trailing closure, I hope it didn’t confuse you)
Manage the self with value
nil
inside the closureIf you think that inside your closure
self
must never benil
then you should captureself
with unowned ([unowned self]). In this way, ifself
isnil
you will an App crash. You should useweak
only ifself
can benil
and it’s perfectly fine in your flow.Let me know if I didn’t get your questions and I’ll be more than happy to answer ✌🏻
14/06/2017 at 19:13
What is your take about using closure to replace the delegate pattern. For example we usually have something like this:
* In sourceVC
destinationVC.delegate = self
In destinationVC
self.delegate.[method in sourceVC and pass in some argument]
Alternatively, we can create a closure in sourceVC and pass it to destinationVC
* In sourceVC
destinationVC.closure = { someArgs in
doSomething
}
destinationVC
var closure = ((someArgsType) -> Void)
// do something then invoke the closure
self.closure(someArgs)
Any pros/cons?
14/06/2017 at 20:21
Hi Andi,
Very good question. My personal thought is: depends 🙂
I like using closures as method parameters, in this way I can manage the callback in the same method scope. If I have a callback which is not related to any method but just something which can be called some time I would go for the delegation. I try explaining better:
I would use a closure as completion handler for having an elegant component interface. An example can be the library
Alamofire
. It wraps a url request in background and you can manage the completion thanks to a closure:Another example which is not a completion handler can be a custom
UITableViewCell
. Here, I may have a configure method:Which I can use like this:
In this example, I like having closures as method argument so I can use in the same scope where I call that method. Of course, here I may have used a delegate with a method
cellCustomButtonDidTap(sender: UITableViewCell)
.When I have several callbacks—like the
UINavigationController
—I go for the delegation otherwise I should have a lot of closure properties and I wouldn’t like it so much. I would prefer having an abstract layer which is the protocol where you create your delegate methods, which keeps the code clean.I don’t know if my answer makes sense to you, since it’s not so easy finding an answer to your question. Because this decision depends on your component interface and on the specific scenario. What’s your thought?