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: MyClassWeak
Optional, becomes nil when deallocated.
weak var obj: MyClass?Unowned
Non-optional, assumes always valid.
unowned let obj: MyClassStrong 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!