What is a closure in Swift and how do you avoid retain cycles?
Answer
A retain cycle occurs when two objects strongly reference each other — ARC can never deallocate them because the count never reaches zero. This is most common with closures that capture self: class ViewController: UIViewController { var timer: Timer? override func viewDidLoad() { timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in self.updateUI() // STRONG capture of self! } } // viewController holds timer, timer's closure holds viewController STRONGLY } // Memory leak!. Solution: capture list with [weak self] or [unowned self]: timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in guard let self = self else { return } // self might be nil self.updateUI() } // OR with unowned (when self is guaranteed to exist): timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [unowned self] _ in self.updateUI() // Crashes if self is nil! }. [weak self] vs [unowned self]: weak — self becomes optional (can be nil); safe when the captured object might be deallocated before the closure; unowned — self is non-optional; use only when the captured object is guaranteed to outlive the closure (crashes if deallocated). Prefer [weak self] in general — safer. Instruments — Leaks: use Xcode Instruments > Leaks tool to detect retain cycles at runtime. Memory Graph Debugger in Xcode also shows cycles visually. The pattern { [weak self] in guard let self = self else { return } ... } is idiomatic Swift for safe closure capture.
Previous
What is Auto Layout and how does it work?
Next
What is the difference between frame and bounds in UIView?