Lesson 44Closures

Capturing Values

What is Capturing?

Closures can "capture" variables from their surrounding scope and keep them alive, even after the original scope is gone!

How Capturing Works

By Reference (default)

Sees changes to original

{ print(value) }

By Value (capture list)

Freezes the value

{ [value] in print(value) }

Capture Lists

{ [capturedVar] in
    // capturedVar is now a copy
}

Key Points

  • Each closure gets its own captured copy
  • Captured values persist after function returns
  • Use capture lists to control capture behavior
  • Be careful of retain cycles (memory leaks)

Common Use Cases

CountersFactoriesCallbacksEvent Handlers
main.swift
// CAPTURING VALUES
// Closures can capture and store references to variables
// from the surrounding context where they're defined

// BASIC EXAMPLE
func makeCounter() -> () -> Int {
    var count = 0  // This variable is "captured"

    let counter = {
        count += 1
        return count
    }

    return counter
}

let counter1 = makeCounter()
print(counter1())  // 1
print(counter1())  // 2
print(counter1())  // 3

// Each counter has its OWN captured count!
let counter2 = makeCounter()
print(counter2())  // 1 (new counter, new count)
print(counter1())  // 4 (original counter continues)

// CAPTURING MULTIPLE VALUES
func makeMultiplier(factor: Int) -> (Int) -> Int {
    // 'factor' is captured
    return { number in
        number * factor
    }
}

let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)

print(double(5))  // 10
print(triple(5))  // 15

// CAPTURING BY REFERENCE
var total = 0

let addToTotal = { (amount: Int) in
    total += amount  // Captures 'total' by reference
}

addToTotal(10)
addToTotal(5)
print(total)  // 15 (original variable is modified!)

// CAPTURE LIST - Capture by value
var value = 10

let captureByReference = { print(value) }
let captureByValue = { [value] in print(value) }

value = 20

captureByReference()  // 20 (sees the change)
captureByValue()      // 10 (captured original value)

// PRACTICAL EXAMPLE: Button actions
func createButton(title: String) -> () -> Void {
    // title is captured
    return {
        print("Button '\(title)' was pressed!")
    }
}

let loginAction = createButton(title: "Login")
let signupAction = createButton(title: "Sign Up")

loginAction()   // Button 'Login' was pressed!
signupAction()  // Button 'Sign Up' was pressed!

// CLOSURE CAPTURES ARE STRONG BY DEFAULT
// This can cause memory issues (retain cycles)
// We'll learn about [weak self] later!

Try It Yourself!

Create a `makeAdder(amount:)` function that returns a closure which adds that amount to any number!