How do Objective-C blocks differ from Swift closures, especially for capture?

4.7.2 · L2 · Interview

Predict first

What do these print? (Both are Objective-C functions returning a block that captured n, then n was set to 99. One variable is __block.)

print(MakeValueCapture()(), MakeBlockCapture()())
  • 1 1
  • 1 99
  • 99 99

Predict first

What does this print for the equivalent Swift closure?

var n = 1
let c = { n }
n = 99
print(c())
  • 1
  • 99

Mental model. A block photographs its variables when it is made. A Swift closure keeps a live link to them, unless the block was told to share with __block.

A block (^{ … }) is Objective-C’s closure, and it imports into Swift as a closure with @convention(block). The key difference is capture semantics. A block captures local variables by value, as read-only copies made when the block is created, unless the variable is declared __block, which shares one mutable variable. A Swift closure captures variables by reference by default, so later changes are seen. The same code therefore returns different values in the two languages.

Other points: a block begins life on the stack and is copied to the heap when stored, which is why a block property uses the copy attribute (ARC does it for you in most cases). A block that captures self strongly and is stored by self is a retain cycle, avoided in the old style with __weak typeof(self) weakSelf = self and a strong local inside the block (the weakify and strongify macros). Imported block parameters become Swift closures, and a nullable block becomes an optional closure.

The plain capture froze n at 1. The __block variable is shared, so the block sees the later 99.

A Swift closure captures the variable itself, not a copy of its value, so it sees 99.

Common traps

  • Mutating a captured local in a block without __block. The compiler rejects it, or the change is to a private copy.
  • Translating a block to Swift literally. The by-value capture you relied on becomes by-reference, so copy the value explicitly with a capture list.

They might follow up with

  • What is the Swift equivalent of __block? Nothing special: every captured var is already shared.
  • How do you get by-value capture in Swift? A capture list: { [n] in … }.

Related questions

  • 1.5.4 What does a capture list like [weak self], [unowned self], [self], or [foo] actually do?
  • 3.3.3 When does a closure create a cycle, and why doesn’t a lazy var closure always?
  • 4.3.3 How does NSCopying work, and why use a copy property for strings and blocks?

← How do you read Objective-C if you only know Swift, and what looks…   What do you need to know to read and safely touch manual reference… →