Why are closures called reference types? What is a closure context?

1.5.3 · L5 · Learning

Predict first

What does this print?

func makeCounter() -> () -> Int {
    var c = 0
    return { c += 1; return c }
}

let a = makeCounter()
let b = a
_ = a()
print(b())
  • 1
  • 2

Mental model. A closure is a backpack: the code plus whatever it packed when it left home. Two closures from the same scope may share the same backpack.

A closure is (conceptually) a heap object holding a function pointer plus captured values — the closure context. The compiler optimizes it away when it can prove the closure does not escape.

Capturing a var may allocate a shared heap box. Multiple closures capturing the same variable share that box, so they see each other's mutations. That is why the classic “loop variable prints the final value” puzzle happens, and why capture lists copy.

Closures are reference types. b is the same closure as a, sharing one context box that holds c.

Common traps

  • “Closures always allocate.” Non-escaping closures with no captures are typically inlined and allocation-free.
  • “Each closure gets its own copy of a captured var.” No — closures created in the same scope share the box unless you capture a copy explicitly.

They might follow up with

  • What does a closure cost? A context allocation, a retain/release pair, and an indirect call — unless optimized.
  • How does a loop capture the current value? Capture it explicitly: { [i] in ... }, or use a helper function that takes i as a parameter.

Related questions

  • 1.5.4 What does a capture list like [weak self], [unowned self], [self], or [foo] actually do?
  • 1.5.5 Why does this print the new city without a capture list, and the old one with [city]?
  • 1.5.6 unowned var view = UIView() then print(view) — what happens?

← guard let self = self else { return } inside a closure — what does it…   What does a capture list like [weak self], [unowned self], [self], or… →