Swift Memory Management
Understanding how Swift manages memory automatically
ð§ What is Memory Management?
Swift automatically manages memory using Automatic Reference Counting (ARC). ARC tracks and manages your app's memory usage by automatically freeing up memory when objects are no longer needed.
// Swift automatically manages this object's memory
class Person {
let name: String
init(name: String) { self.name = name }
}
let person = Person(name: "John") // Memory allocated
// Memory automatically freed when person goes out of scope
Key Memory Concepts
Reference Counting
ARC counts references to objects
var ref1: Person? = Person(name: "Alice") // Count: 1
var ref2 = ref1 // Count: 2
ref1 = nil // Count: 1
ref2 = nil // Count: 0, memory freed
Strong References
Default references that keep objects alive
class Dog {
var owner: Person // Strong reference
init(owner: Person) { self.owner = owner }
}
Weak References
Don't keep objects alive, prevent cycles
class Dog {
weak var owner: Person? // Weak reference
init(owner: Person) { self.owner = owner }
}
Reference Cycles
When objects reference each other
// Avoid cycles with weak references
class Parent {
var children: [Child] = []
}
class Child {
weak var parent: Parent?
}
ðđ Strong vs Weak References
Understanding the difference prevents memory leaks:
class Owner {
var pet: Pet?
let name: String
init(name: String) {
self.name = name
}
}
class Pet {
weak var owner: Owner? // Weak to prevent cycle
let name: String
init(name: String) {
self.name = name
}
}
// Usage
var john: Owner? = Owner(name: "John")
var buddy: Pet? = Pet(name: "Buddy")
john?.pet = buddy
buddy?.owner = john
john = nil // Owner can be freed because pet uses weak reference
ðđ Unowned References
Use when you know the reference will never be nil:
class CreditCard {
let number: String
unowned let customer: Customer // Always has a customer
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
}
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
}
// Usage
let customer = Customer(name: "Alice")
customer.card = CreditCard(number: "1234", customer: customer)
ðđ Closures and Memory
Closures can create reference cycles too:
class ViewController {
var name = "Main"
func setupButton() {
// Strong reference cycle - BAD
button.onTap = {
print(self.name) // Captures self strongly
}
// Weak reference - GOOD
button.onTap = { [weak self] in
print(self?.name ?? "Unknown")
}
// Unowned reference - GOOD (if self always exists)
button.onTap = { [unowned self] in
print(self.name)
}
}
}
ðđ Memory Best Practices
Follow these guidelines for efficient memory usage:
â Do:
- Use weak references for delegates and parent-child relationships
- Use unowned references when you're sure the reference won't be nil
- Use capture lists in closures to avoid cycles
- Set references to nil when done with objects
â Don't:
- Create strong reference cycles between objects
- Ignore memory warnings in your apps
- Keep unnecessary strong references to large objects