Skip to content
Memory Management with ARC
step 1/7

Reading — step 1 of 7

Learn

~3 min readConcurrency

Swift uses Automatic Reference Counting (ARC) — not garbage collection. The compiler inserts retain/release calls so reference-typed objects are deallocated when no one holds them. Most of the time it's invisible. The exception: reference cycles, which require careful handling.

ARC basics

For reference types (classes), Swift tracks how many references each instance has:

class User {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) created")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func demo() {
    let u = User(name: "Alice")
    // u goes out of scope here — refcount drops to 0 — deinit runs
}
func main() {
    demo()
    // prints: Alice created / Alice deallocated
}

Value types (structs, enums) don't use ARC — they're copied on assignment, no shared ownership to track.

Strong references — the default

A normal property is a strong reference:

class Person {
    var car: Car?         // strong — Person owns Car
}

class Car {
    var owner: Person?    // strong — Car owns Person?? trouble incoming
}

Reference cycles — the classic ARC bug

let p = Person()
let c = Car()
p.car = c
c.owner = p
// now: p strongly references c. c strongly references p.
// Both refcounts are 2. They never reach 0.
// Both leak — even after p and c go out of scope.

No deallocation happens because the two objects keep each other alive. Memory leak.

weak references — break the cycle

Mark one direction weak to opt out of refcount bumps:

class Person {
    var car: Car?            // strong
}

class Car {
    weak var owner: Person?  // weak — doesn't bump refcount
}

Now c.owner doesn't keep p alive. When p's strong references go away, p is deallocated and c.owner automatically becomes nil.

weak references are always Optional because the target can become nil at any time.

unowned references — "I know it'll outlive me"

Like weak but non-optional. Use when you're certain the target lives at least as long as the reference holder:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: String
    unowned let customer: Customer    // card can't exist without customer
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

If you're wrong and the target dies first, accessing unowned reference crashes (similar to force-unwrapping a nil Optional). Use sparingly.

Closures capture self strongly — careful

class NetworkClient {
    func fetch(completion: @escaping (Data) -> Void) {
        // ...
    }
    
    func loadProfile() {
        fetch { data in
            self.process(data)    // captures self strongly!
        }
    }
}

If fetch's closure outlives the NetworkClient, the closure keeps it alive — possibly forever. Fix with a capture list:

fetch { [weak self] data in
    self?.process(data)        // weak — closure doesn't keep self alive
}

[weak self] and [unowned self] are the canonical patterns for callbacks that escape your method.

When to worry about ARC

In most Swift code, ARC is invisible. You worry when:

  • Two classes hold each other → use weak or unowned on one side
  • Closures escape (@escaping) → use [weak self]
  • Long-lived parent ↔ short-lived child relationships
  • Profiling shows leaks (Instruments → Allocations)

Value-type-heavy code (most Swift) sees ARC mostly via class members deeply nested. Structs and enums don't have these issues.

Common mistakes

  • Forgetting [weak self] in escaping closures — leaks and prevents deallocation.
  • Using unowned when the target might die first — crash.
  • Marking value types weak — compile error. weak/unowned are for class references only.
  • Trying to debug "leak" symptoms in Swift like in C++ — different tools. Use Instruments' Leaks and Allocations templates.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…