SwiftMidOpen-ended
How do closure capture lists prevent retain cycles and memory leaks?
Explanation & Code
Answer:
Closure capture lists define explicit ownership rules for references captured inside a closure's body, breaking retain cycles by capturing instances as weak or unowned.
By default, closures in Swift capture any referenced object with a strong reference. If the object also holds a reference to the closure (directly or indirectly through a chain of objects), a strong reference cycle is formed.
[weak self]: Creates a zeroing weak reference. The captured reference becomes anOptional(Self?). When the referenced object deallocates, the pointer is automatically set tonil.[unowned self]: Creates a non-optional reference that assumes the referenced object will never benilwhile the closure executes. If called after the object is deallocated, it triggers a runtime crash (trap).- Swift 5.3+
guard let self: Allows safely unwrapping[weak self]withguard let self else { return }, creating a temporary local strong reference for the duration of closure execution.
Code Example:
class OrderService {
var orderStatus: String = "Pending"
var onComplete: (() -> Void)?
func placeOrder() {
// [weak self] breaks the retain cycle between self and onComplete
onComplete = { [weak self] in
guard let self else { return }
self.orderStatus = "Confirmed"
self.notifyAnalytics()
}
}
private func notifyAnalytics() {
print("Order confirmed: \(orderStatus)")
}
}
When to choose weak vs unowned:
- Use
[weak self]whenever the closure can outlive the referenced object (async network requests, event listeners, dispatch queues). - Use
[unowned self]ONLY when the closure's lifecycle is strictly bounded by the owner's lifecycle and will never execute after the owner deallocates (e.g., synchronous animation blocks or strictly managed parent-child pairs).
Rate your understanding: