When you use closures in Swift, they can keep references to variables outside them. This can affect how memory is used and when things get cleaned up.
Memory implications of captures in Swift
let closure = { [captureList] in // code using captured variables }
The captureList lets you control how variables are captured (strongly, weakly, or unowned).
Without a capture list, variables are captured strongly by default, which can cause retain cycles.
value strongly by default.var value = 10 let closure = { print(value) } closure()
[weak self] avoids a strong reference cycle between the closure and the class instance.class MyClass { var number = 5 lazy var closure: () -> Void = { [weak self] in print(self?.number ?? 0) } } let obj = MyClass() obj.closure()
[unowned] captures without increasing reference count but assumes the variable will exist when used.class Counter { var count = 0 } let counter = Counter() let closure = { [unowned counter] in print(counter.count) } closure()
This program shows how capturing person weakly in the closure allows person to be deinitialized after testClosure() ends.
class Person { let name: String init(name: String) { self.name = name print("Person \(name) created") } deinit { print("Person \(name) is being deinitialized") } } func testClosure() { let person = Person(name: "Alex") let closure = { [weak person] in if let p = person { print("Hello, \(p.name)!") } else { print("Person no longer exists") } } closure() } testClosure()
Strong captures keep objects alive, which can cause memory leaks if not handled carefully.
Use [weak] or [unowned] capture lists to avoid retain cycles.
Weak captures make the captured variable optional inside the closure.
Closures capture variables and can affect memory by keeping objects alive.
Use capture lists to control how variables are captured and avoid memory leaks.
Understanding capture behavior helps write safer and more efficient Swift code.
Practice
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 AQuick Check:
Strong capture = possible memory leak [OK]
- Thinking variables are copied by default
- Assuming closures do not hold references
- Believing variables auto-release on capture
self in a Swift closure?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 DQuick Check:
Capture list uses square brackets [weak self] [OK]
- Using parentheses instead of square brackets
- Placing 'weak' outside capture list
- Incorrect arrow syntax in capture list
class Person {
var name = "Alice"
lazy var greet: () -> Void = { [unowned self] in
print("Hello, \(self.name)!")
}
}
let p = Person()
p.greet()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 CQuick Check:
Unowned capture prints value safely if object exists [OK]
- Expecting Optional wrapping in output
- Thinking unowned causes runtime error immediately
- Assuming empty string prints
class ViewController {
var title = "Main"
lazy var printTitle = {
print(self.title)
}
}
let vc = ViewController()
vc.printTitle()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 BQuick Check:
Strong capture in closures can cause retain cycles [OK]
- Ignoring retain cycles in lazy properties
- Assuming no capture means no memory issues
- Confusing syntax errors with memory issues
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?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:
Capturedataweakly and unwrap it safely inside the closure. -> Option AQuick Check:
Weak capture + safe unwrap = safe memory handling [OK]
- Using unowned and risking runtime crashes
- Strong capture causing retain cycles
- Ignoring safe unwrapping of weak references
Ready to prove it?
Pass the test and this pattern moves unknown
