Swift Memory Management

Swift Memory Management

Understand ARC, avoid retain cycles with weak/unowned, and manage closure captures safely.


Automatic Reference Counting (ARC)

Classes are reference types.

Swift uses ARC to automatically track and release class instances when no strong references remain.

Example

class Box {
  let name: String
  init(_ n: String) { name = n; print("init \(n)") }
  deinit { print("deinit \(name)") }
}

do {
  let b = Box("A")
  print("in scope")
}
print("after scope")

This example demonstrates ARC’s automatic deallocation of the Box instance when it goes out of scope.

Tip: Use weak to avoid strong reference cycles between class instances.


Strong Reference Cycles

strong reference cycle occurs when two class instances hold strong references to each other, preventing ARC from deallocating them.

Mark one side as weak (or unowned when appropriate) to break the cycle.

Example

class Person {
  let name: String
  var apartment: Apartment?
  init(name: String) { self.name = name }
  deinit { print("Person deinit") }
}

class Apartment {
  let unit: String
  weak var tenant: Person? // weak breaks the cycle
  init(unit: String) { self.unit = unit }
  deinit { print("Apartment deinit") }
}

do {
  var john: Person? = Person(name: "John")
  var unit: Apartment? = Apartment(unit: "4A")
  john!.apartment = unit
  unit!.tenant = john
  john = nil   // Person deinit
  unit = nil   // Apartment deinit
}

Declaring tenant as weak breaks the cycle so both objects deallocate when their strong references are set to nil.



Closures and Capture Lists (weak self)

Closures capture variables by default.

When a class stores a closure that references self, use a capture list like [weak self] to avoid retain cycles.

Example

class Loader {
  var onComplete: (() -> Void)?
  func load() {
// simulate async completion
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
  guard let self = self else { return }
  print("Finished: \(self)")
  self.onComplete?()
}
} deinit { print("Loader deinit") } } do { let loader = Loader() loader.onComplete = { print("done callback") } loader.load() } // loader can be deallocated if nothing else references it

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *