Skip to content
AgileLogics
6 min read

Memory Management with ARC

Prerequisites: Classes and reference types, Structs vs classes

Swift manages the memory of class instances with Automatic Reference Counting (ARC). Every class instance keeps a count of how many strong references point to it. When that count drops to zero, the instance is freed immediately and its deinit runs.

Real-world analogy: a meeting room booking. Each person who needs the room adds their name to the list; when the last name is crossed off, the room is released at once — no cleaner doing periodic sweeps.

Watching ARC work

final class Session {
    let user: String
    init(user: String) {
        self.user = user
        print("\(user): session started")
    }
    deinit { print("\(user): session ended") }
}

var first: Session? = Session(user: "Ada")   // count: 1
var second = first                           // count: 2
print("Both set: \(first != nil && second != nil)")
first = nil                                  // count: 1
print("Still reachable: \(second?.user ?? "gone")")
second = nil                                 // count: 0 → freed
print("Done")
Ada: session started
Both set: true
Still reachable: Ada
Ada: session ended
Done

The session isn't freed when first is cleared — second still refers to it. It's freed the moment the last strong reference goes away.

ARC vs garbage collection

Languages like Java, C#, and Go use a garbage collector that periodically finds and frees unused objects. ARC works differently:

  • Memory is freed deterministically, at a predictable point — as soon as the last reference disappears. There are no collection pauses.
  • The compiler inserts the counting code for you at compile time.
  • The trade-off: ARC can't free two objects that refer to each other, even when nothing else uses them. That's a reference cycle — the next lesson.

Value types and ARC

ARC only applies to reference types (classes, actors, and closures). Structs and enums are copied rather than shared, so there's nothing to count — one more reason Swift favors value types. (Types like Array and String use reference-counted storage internally, but that's hidden behind their value semantics.)

Common mistakes

  • Assuming Swift has no memory leaks because it manages memory automatically. Reference cycles leak — see the next lesson.
  • Relying on deinit running at an exact line in optimized builds. The compiler may end a reference's lifetime right after its last use, slightly earlier than the end of the scope.