📖 Scenario: Imagine you are building a simple Swift app that uses closures to handle tasks. Understanding how closures capture variables helps you avoid memory problems like strong reference cycles.
🎯 Goal: You will create a closure that captures a variable, then modify the capture to use a weak reference to prevent memory issues.
📋 What You'll Learn
Create a variable called counter with initial value 0
Create a closure called increment that captures counter and increases it by 1
Create a weak capture of self inside a closure to avoid strong reference cycles
Print the value of counter after calling the closure
💡 Why This Matters
🌍 Real World
Closures are used in Swift apps for callbacks, event handling, and asynchronous tasks. Understanding how they capture variables helps prevent memory leaks.
💼 Career
Swift developers must manage memory carefully. Knowing how closures capture variables and how to use weak captures is essential for writing efficient, bug-free apps.
Progress0 / 4 steps
1
Create a variable counter with value 0
Create a variable called counter and set it to 0.
Swift
Hint
Use var counter = 0 to create a variable that can change.
2
Create a closure increment that increases counter
Create a closure called increment that captures counter and adds 1 to it each time it is called.
Swift
Hint
Use let increment = { counter += 1 } to create the closure.
3
Call the increment closure three times
Call the closure increment exactly three times to increase counter.
Swift
Hint
Call increment() three times, each on its own line.
4
Print the value of counter
Write a print statement to display the value of counter.
Swift
Hint
Use print(counter) to show the current value.
Practice
(1/5)
1. What happens when a Swift closure captures a variable strongly by default?
easy
A. The closure keeps a strong reference, potentially causing memory leaks if not handled.
B. The variable is copied and changes inside the closure do not affect the original.
C. The closure does not keep any reference to the variable.
D. The variable is automatically released when the closure is created.
Solution
Step 1: Understand default capture behavior
By default, Swift closures capture variables strongly, meaning they keep a reference to the original variable.
Step 2: Recognize memory implications
If the closure and the captured variable reference each other, it can cause a retain cycle, leading to memory leaks.
Final Answer:
The closure keeps a strong reference, potentially causing memory leaks if not handled. -> Option A
2. Which syntax correctly declares a capture list to weakly capture a variable self in a Swift closure?
easy
A. { (weak self) in print(self) }
B. { self weak in print(self) }
C. { weak self -> Void in print(self) }
D. { [weak self] in print(self) }
Solution
Step 1: Recall capture list syntax
Capture lists are declared inside square brackets before the closure parameters, e.g., [weak self].
Step 2: Match correct syntax
{ [weak self] in print(self) } uses the correct syntax: { [weak self] in ... }.
Final Answer:
{ [weak self] in print(self) } -> Option D
Quick Check:
Capture list uses square brackets [weak self] [OK]
Hint: Capture lists use square brackets before closure parameters [OK]
Common Mistakes:
Using parentheses instead of square brackets
Placing 'weak' outside capture list
Incorrect arrow syntax in capture list
3. What will be printed by the following Swift code?
class Person {
var name = "Alice"
lazy var greet: () -> Void = { [unowned self] in
print("Hello, \(self.name)!")
}
}
let p = Person()
p.greet()
medium
A. Hello, !
B. Hello, Optional("Alice")!
C. Hello, Alice!
D. Runtime error due to unowned reference
Solution
Step 1: Analyze closure capture
The closure captures self as unowned, meaning it expects self to always exist when called.
Step 2: Check code execution
Instance p is created, greet closure is called, printing "Hello, Alice!" using self.name.
Final Answer:
Hello, Alice! -> Option C
Quick Check:
Unowned capture prints value safely if object exists [OK]
Hint: Unowned captures assume object exists, no optional needed [OK]
Common Mistakes:
Expecting Optional wrapping in output
Thinking unowned causes runtime error immediately
Assuming empty string prints
4. Identify the problem in this Swift code snippet and how to fix it:
class ViewController {
var title = "Main"
lazy var printTitle = {
print(self.title)
}
}
let vc = ViewController()
vc.printTitle()
medium
A. No problem, code runs fine and prints "Main".
B. Retain cycle due to strong self capture; fix with [weak self] capture list.
C. Syntax error: missing capture list brackets.
D. Runtime error: self is nil inside closure.
Solution
Step 1: Identify capture type
The closure captures self strongly by default, which can cause retain cycles if ViewController holds the closure.
Step 2: Fix with weak capture
Adding [weak self] in the capture list prevents retain cycle by capturing self weakly.
Final Answer:
Retain cycle due to strong self capture; fix with [weak self] capture list. -> Option B
Quick Check:
Strong capture in closures can cause retain cycles [OK]
Hint: Use [weak self] to avoid retain cycles in closures [OK]
Common Mistakes:
Ignoring retain cycles in lazy properties
Assuming no capture means no memory issues
Confusing syntax errors with memory issues
5. You have a Swift class with a closure property that captures a variable data. You want to ensure the closure does not keep data alive if the class instance is deallocated, but still access data safely inside the closure. Which capture strategy should you use?
hard
A. Capture data weakly and unwrap it safely inside the closure.
B. Capture data unowned without checks inside the closure.
C. Capture data strongly to keep it alive as long as the closure exists.
D. Do not capture data at all; pass it as a parameter instead.
Solution
Step 1: Understand capture effects on memory
Strong capture keeps data alive, which may cause memory leaks if the class is deallocated.
Step 2: Choose safe capture method
Weak capture allows data to be nil if deallocated; unwrapping safely avoids crashes.
Step 3: Compare with unowned and no capture
Unowned assumes data always exists, risking crashes. Not capturing requires passing data every time, which may be inconvenient.
Final Answer:
Capture data weakly and unwrap it safely inside the closure. -> Option A