How do Objective-C blocks differ from Swift closures, especially for capture?
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 capturedvaris 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… →