Welcome to Lesson 4 of Intro to Functional Swift. Today, you will discover a unique behavior of closures, and how to make it right.
First, let us create two variables.
var a = 0
var b = 0
Create a closure block which prints a
and b
.
let newClosure = { print(a, b) }
newClosure() // (0, 0)
Create an array whose type is [() -> ()]
. newClosure
will be added to closureArray
.
var closureArray: [() -> ()] = []
var i = 0
Let us add multiple closure blocks to the array using a for-in
loop.
for _ in 1...5 {
closureArray.append {
print(i)
}
i += 1
}
// i is 5 by now
Let us call the function.
closureArray[0]() // 5 😲
closureArray[1]() // 5 🤔
closureArray[2]() // 5 😨
closureArray[3]() // 5 😭
closureArray[4]() // 5 😱
You might have expected each element to print, 0
, 1
, 2
, 3
, 4
. However, each closure uses the final value of i
which is 5 at the end of the loop.
Important: A closure block is a reference type. You will learn more in Chapter 5.
Let us dig deeper.
var c = 0
var d = 0
let smartClosure: () -> () = { _ in
print(c, d)
}
First, modify the value of c
and d
. Second execute the closure.
c = 9
d = 9
smartClosure() // (9, 9)
smartClosure()
prints (9, 9)
. When the value of c
and d
mutate, the change is reflected on the closure by default.
Let us destroy the default behavior.
Strategy: Do not reference. Copy
Add an array [a, b]
before in
. The array is referred to as a capture list.
let smarterClosure: () -> () = { [c, d] in
print(c, d)
}
smarterClosure() // (9, 9)
Now , attempt to modify the value of c
and d
c = 10
d = 10
Execute smarterClosure
. You will discover that the change is not reflected on the closure anymore.
smarterClosure() // (9, 9)
The capture list "copies" the value of c
and d
at the particular time the closure was created. It become invincible.
Let us go back to the early example with the for-in
loop to append the closure block to the array.
var smartClosureArray: [() -> ()] = []
var j = 0
Capture j
for _ in 1...5 {
smartClosureArray.append { [j] in
print(j)
}
j += 1
}
You may rebrand j
. In this case, I will refer to j
as num
within the closure block.
for _ in 1...5 {
smartClosureArray.append { [num = j] in
print(num)
}
j += 1
}
Let us find out whether you've killed the default behavior.
smartClosureArray[0]() // 0 ☝️
smartClosureArray[1]() // 1 💪
smartClosureArray[2]() // 2 🎁
smartClosureArray[3]() // 3 🎉
smartClosureArray[4]() // 4 🎅
You've learned the default behavior of a closure block which is identical to that of instances created with class objects. In fact, closures are reference types. In Chapter 5, however, you will discover some of problems that may arise due to the default behavior. Stay tuned.
In the following lesson, you will learn how to beautify closures.
Note: Learn Swift with Bob is available on Udemy. If you wish to receive a discount link, you may sign up here.