Swift Capture List in Closures

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

Bob Lee
Bob the Developer
4 min readMar 18, 2017

--

You will find out

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 = 0for _ 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: [() -> ()] = []

The content has been fully migrated from Medium to the personal blog. If you wish to finish reading the rest of the article and learn about capture list and reference type, feel free to visit the new platform here.

--

--