← All topics/Swift language features

Question 4

Memory management: ARC, strong, weak, unowned

Explain ARC's role in Swift. When do you use weak vs unowned references, and how do you break retain cycles in closures and delegates?

Answer outline

ARC counts strong references to class instances on the heap, and when the count hits zero, deinit runs. A struct itself isn't reference counted, but any class reference stored inside it still is.

weak is optional and becomes nil when the object is deallocated. Use it for delegates and closure captures where the referenced object may be released before the reference is used.

unowned is non-optional and assumes the object it points to outlives the reference. Accessing it after deallocation crashes, so only use it when that lifetime relationship is guaranteed.

Closures capture strongly by default, so when self stores a closure that uses self, the two keep each other alive. Break the cycle with [weak self] or [unowned self] in a capture list, which goes before the closure's parameter list.

Principles

  • Prefer weak over unowned, because unowned crashes if the object is already gone when you use it.
  • Delegate properties must be weak, since a strong delegate creates a retain cycle.
  • Escaping closures capture self strongly, so add [weak self] whenever the closure may outlive its owner.
  • Timers, notification observers, and network callbacks are the classic sources of retain cycles.
Weak delegate pattern
protocol DetailDelegate: AnyObject {
    func onDone()
}

final class DetailViewController {
    weak var delegate: DetailDelegate?
}
Closure capture
fetch { [weak self] result in
    guard let self else { return }
    self.apply(result)
}

Follow-up angles

  • @MainActor doesn't remove ARC concerns. Escaping closures still need [weak self] wherever a cycle is possible.