Swift capture lists

Derrick Ho
2 min readAug 20, 2016

--

Objective-c with the power of ARC has eliminated most memory leaks but left us with one: blocks capturing a reference to self.

Swift uses ARC and has the same issue. You can get around it by using unowned self when you write the block. However, this article is not about retain cycles. The purpose of this article is to show a neat trick regarding swift’s capture lists.

Before that, let me lay out some background. There is something called the JavaScript infamous Loop Issue where a loop that creates a closure would end up using the wrong value!

I have reproduced the issue in swift

var arr = [() -> ()]()
var i = 0
while i < 5 {
arr.append({
print(i)
})
i += 1
}
for i in arr {
i()
}

In the above example i am creating a block that will print i and then running each block. Althought I expect the computer to print 0,1,2,3,4 it doesn’t happen. Instead it prints 5,5,5,5,5. What is going on here? Basically the closure is capturing the reference to i, but the value of i keeps changing from 0 to 5 so when you call all the closures, all of them point to the same reference.

Not all is lost. Swift offers us two solutions to this problem.

var arr = [() -> ()]()
var i = 0
while i < 5 {
let block = { (num: Int) -> () in
arr.append { [num = i] in
print(num)
}
}
block(i)
i += 1
}
for i in arr {
i()
}

If you wrap the closure in another and call it it will act correct. In the above example the closure is capturing the reference to num. At the end of each loop, num is not owned by any one except for the block. Now each block will print a different value because the reference to the num variable is different!

The second solution works exactly the same way except we are using capture lists to do the hard work.

var arr = [() -> ()]()
var i = 0
while i < 5 {
arr.append({ [num = i] in
print(num)
})
i += 1
}
for i in arr {
i()
}

As you can see, the capture list is doing the exact same work as the previous example.

You could say that this is a more swifty solution.

And there is one more thing…

There is actually a 3rd solution…

var arr = [() -> ()]()
var i = 0
while i < 5 {
let num = i
arr.append({
print(num)
})
i += 1
}
for i in arr {
i()
}

This may be the reason apple is pushing us developers to use let instead of var.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response