CloseHomeAboutBlogCourse
Bob I’m an iOS instructor/blogger from S.Korea.July 14 • 5 min read • Edit

Swift Capture List in Closures

Closure is a reference type, so use [weak self] to prevent memory leak. Wait, what?

Greeting

Welcome back my lovely readers. It’s nice to see you here. Recently, I’ve been writing closure related topics including completion handler and functional programming since they were most dreaded topics based on the survey I conducted a month ago with you guys. As you may have realized, an iOS app is filled with closures and we can’t get away without them just like our impending death and tax. If you are not comfortable with closures, you might find Learn Swift with Bob useful for this tutorial.

Motivation

You’ve probably heard that Swift is my first programming language. I may relate to what many of you go through. When I attempted to learn closures, I’ve found myself frustrated even though I’ve done quite a bit of research. I’m aware there are a lot of you out there like myself. Well, you’ve come to the right place. I’m here to help. Let’s get started. You might have seen something like below if you’ve worked with closures before.

var random: () -> String = { [weak self] in
  return "Hello world"
}

I understand what's in your head. You probably be like,

What the hell is that bracket — Me

Don’t worry. I’m not even going to start with weak. In fact, in this tutorial, I refuse to talk about weak nor self. Let’s understand why the Swift engineers have created the mysterious bracket in closures. Let’s dive in.

Closure Characteristics

First of all, we have to understand the nature of Swift closures in order to appreciate the mysterious creature. Let’s create a closure that does nothing but print something.

var a = 0
var b = 0

// Type explicitly stated
let newClosure: () -> () = { print(a, b) }

// Type inferred
let closure = { print(a, b) }

Let’s call it.

closure() // 0 0

Now, let’s change the variables

a = 6
b = 9

Let’s call it.

closure() // 6 9

Wow, the closure prints different values! It may seem intuitive. Yeah, it is. The closure has a reference/connection to the variable a and b. So, when you make a change to these variables, the closure will reflect that. a.k.a closures are also known as a reference type.

Reference type is like referring to the answer keys in a textbook. If you want to know more about reference type vs value type, feel free to read the intro here.

Problem with Reference Type

However, it may not always behave intuitively. In fact, it could lead to a disaster. I will show you one. First, let’s create an array that contains a closure whose type is () -> ().

var closureArray: [() -> ()] = []

Now, let’s append closures to closureArray.

var i = 0
for _ in 1...5 {
closureArray.append { print(i) }
i += 1
}

Now, you attempt to call each closure, and you probably expect each of them to print 1 through 5. You wish or I wish. Nope.

closureArray[0]() // 5 😲
closureArray[1]() // 5 🤔
closureArray[2]() // 5 😨
closureArray[3]() // 5 😭
closureArray[4]() // 5 😱

So, what’s going on? Well, each closure is referencing the final value of i which is 5 by the end of the for-in loop. So yeah, that’s a big problem. This is messed up. There must be a better way. Lol, I’m talking to myself.

Don’t Reference, Steal

Let’s go back to our very first example. Instead of referencing, you may create your own copy of a variable(s) within a closure block by adding that monster. I know it doesn’t mean much at this point, so let’s take a look.

First, create two variables.

var c = 0
var d = 0

Second, create a closure.

let smartClosure: () -> () = { _ in
print(c, d)
}

The closure above is identical to the first example. It just looks different since you may express closures in various ways.

If you don’t understand what I just said above, please leave and come back after receiving some bolts and nuts via here.

However, to differentiate from the first example, you are going to add that mysterious monster right before the in block.

let smartClosure: () -> () = { [c, d] in
print(c, d)
}

Let’s see if it works before I explain.

c = 6
d = 9

Let’s call it.

smartClosure() // 0 0 👍

Now, let me explain. The mysterious monster is an array as you might have guessed from how it looks. When you enter variables in the monster right before the keyword in, the closure is no longer referencing the original variables Instead, the closure steals that shit and creates its own copy within the closure block.

Therefore, even if you manipulate the original variables, the closure doesn’t care since it is no longer referencing, and it’s independent.

The array, [] is called a captured list for some reason. To visualize, the closure block is like a pirate in the sea trying to steal items from countries and other ships.

Solve the Problem

Since you’ve learned something, let’s go back to the second example in which we created an array that contains closures.

var smartClosureArray: [() -> ()] = []

Now, we are going to create a for-in loop identical to the previous example. But, there is just one difference. Right, we are going to add a pirate (I will no longer call it as a mysterious monster).

var j = 0
for _ in 1...5 {
  smartClosureArray.append { [j] in
    print(j)
  }
  j += 1
}

Yup, now, each closure steals the value of j at each loop and stores onto its own closure block or ship. As a result, there should be 5 different j for each closure.

Let’s check.

smartClosureArray[0]() // 0 ☝️
smartClosureArray[1]() // 1 💪
smartClosureArray[2]() // 2 🎁
smartClosureArray[3]() // 3 🎉
smartClosureArray[4]() // 4 🎅

But sometimes, when you steal items, you don’t necessarily want to call those items as if they come from anywhere else. Well, not only you can steal, but you can also rebrand and market as Steve Jobs love to quote Picasso’s saying, “Good artists copy, great artists steal.”

var j = 0
for _ in 1...5 {
  smartClosureArray.append { [num = j] in
    print(num)
  }
  j += 1
}

Now, you have arbitrarily created a variable called num, you may use it within the closure block or ⛴.

smartClosureArray[0]() // 0 ☝️
smartClosureArray[1]() // 1 💪
smartClosureArray[2]() // 2 🎁
smartClosureArray[3]() // 3 🎉
smartClosureArray[4]() // 4 🎅

Last Message

I’m not going to lie. We haven’t much dived into the practical aspects of a capture list along with memory management such as [weak self] and retention cycle in closures, unowned and so on, make sure follow me on Medium or Facebook for the next article. If you wanna get to know me at a personal level, you can see my story on Instagram. @bobthedev If you enjoyed the article, and want to learn from me and get more Swift tutorials, you might find Learn Swift with Bob great for your learning.

Source Code

About Me

iOS Developer from South Korea. Feel free to follow my story on Instagram or get serious on LinkedIn