Lesson 68Classes

ARC & Memory Management

What is ARC?

Automatic Reference Counting (ARC) tracks how many references point to each class instance. When count reaches zero, memory is freed automatically!

Reference Types

Strong

Default. Keeps object alive.

var obj: MyClass

Weak

Optional, becomes nil when deallocated.

weak var obj: MyClass?

Unowned

Non-optional, assumes always valid.

unowned let obj: MyClass

Strong Reference Cycles

Memory leaks occur when two objects hold strong references to each other!

A → B (strong) && B → A (strong) = Memory Leak!

When to Use Each

strong: Parent → Child relationships
weak: Child → Parent, delegates, optional refs
unowned: When ref outlives or equals owner's lifetime

Capture Lists in Closures

Closures capture self strongly by default. Use capture lists to break cycles:

{ [weak self] in ... } or { [unowned self] in ... }
main.swift
// AUTOMATIC REFERENCE COUNTING (ARC)
// Swift automatically manages memory for classes

// HOW ARC WORKS
class Person {
    let name: String

    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

// Reference count starts at 1
var reference1: Person? = Person(name: "John")
// "John is being initialized"

// Reference count becomes 2
var reference2 = reference1

// Reference count becomes 3
var reference3 = reference1

// Reference count drops to 2
reference1 = nil

// Reference count drops to 1
reference2 = nil

// Reference count drops to 0 - deinit called!
reference3 = nil
// "John is being deinitialized"

// STRONG REFERENCE CYCLES (Memory Leak!)
class Apartment {
    let unit: String
    var tenant: Tenant?

    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) deinitialized") }
}

class Tenant {
    let name: String
    var apartment: Apartment?  // Strong reference

    init(name: String) { self.name = name }
    deinit { print("Tenant \(name) deinitialized") }
}

var apt: Apartment? = Apartment(unit: "4A")
var person: Tenant? = Tenant(name: "Alice")

apt?.tenant = person
person?.apartment = apt  // Creates a cycle!

apt = nil      // Won't deallocate!
person = nil   // Won't deallocate! Memory leak!

// FIXING WITH WEAK REFERENCES
class ApartmentFixed {
    let unit: String
    weak var tenant: TenantFixed?  // Weak reference

    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) deinitialized") }
}

class TenantFixed {
    let name: String
    var apartment: ApartmentFixed?

    init(name: String) { self.name = name }
    deinit { print("Tenant \(name) deinitialized") }
}

// UNOWNED REFERENCES
// Use when reference should never be nil during its lifetime
class Customer {
    let name: String
    var card: CreditCard?

    init(name: String) { self.name = name }
    deinit { print("\(name) deinitialized") }
}

class CreditCard {
    let number: Int
    unowned let customer: Customer  // Always has a customer

    init(number: Int, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) deinitialized") }
}

var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: 1234, customer: john!)
john = nil  // Both deallocated properly!

// CLOSURES AND CAPTURE LISTS
class HTMLElement {
    let name: String
    let text: String?

    // Strong reference cycle with closure!
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit { print("\(name) is being deinitialized") }
}

// Fix with capture list
class HTMLElementFixed {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit { print("\(name) is being deinitialized") }
}

Try It Yourself!

Create a Team and Player class where players reference their team. Use the correct reference type to avoid memory leaks!