Swift ARC (Automatic Reference Counting) – A Complete Guide
Introduction
In Swift, Automatic Reference Counting (ARC) is a memory management system that automatically tracks and manages the memory used by objects. It ensures that objects are deallocated when they are no longer needed, preventing memory leaks and optimizing performance.
Unlike manual memory management in older languages, Swift's ARC automatically allocates and deallocates memory for class instances, making it more efficient and reducing developer effort. Understanding ARC is crucial for writing high-performance, memory-efficient Swift applications.
Why is ARC Important in Swift?
- Prevents Memory Leaks: ARC ensures memory is released when objects are no longer in use.
- Optimized Performance: Automatically deallocates unused objects, improving app efficiency.
- No Manual Memory Management: Unlike languages like C++, Swift handles memory automatically.
- Reduces Retain Cycles: ARC helps prevent strong reference cycles that lead to memory leaks.
How ARC Works in Swift?
ARC keeps track of how many references point to an object. When an object’s reference count reaches zero, Swift automatically deallocates it.
Reference Counting Example
import Foundation
class Person {
let name: String
init(name: String)
{
self.name = name
print("\(name)
is initialized")
}
deinit {
print("\(name)
is being deallocated")
}
}
var person1: Person? = Person(name: "John")
person1 = nil // Reference count
becomes zero, so object is deallocated
Output
John is being deallocated
Types of References in ARC
1. Strong References (Default Reference Type)
By default, references in Swift are strong references, meaning the object remains in memory as long as a reference exists.
Example of a Strong Reference Retain Cycle
class Student {
let name: String
var teacher: Teacher?
init(name: String)
{
self.name = name
}
deinit {
print("\(name)
is being deallocated")
}
}
class Teacher {
let name: String
var student: Student?
init(name: String)
{
self.name = name
}
deinit {
print("\(name)
is being deallocated")
}
}
var student1: Student? = Student(name: "Alice")
var teacher1: Teacher? = Teacher(name: "Mr. Smith")
student1?.teacher = teacher1
teacher1?.student = student1
// Even when we set both to nil, the objects are not deallocated due to strong
reference cycle
student1 = nil
teacher1 = nil
Output (No deallocation due to memory leak)
How to Fix Strong Reference Cycles in Swift?
2. Weak References (weak
Keyword)
A weak reference does not increase the reference count, breaking the strong reference cycle.
Example Using weak
to Prevent Memory Leak
class Student {
let name: String
weak var teacher: Teacher? // weak reference to avoid retain cycle
init(name: String)
{
self.name = name
}
deinit {
print("\(name)
is being deallocated")
}
}
class Teacher {
let name: String
var student: Student?
init(name: String)
{
self.name = name
}
deinit {
print("\(name)
is being deallocated")
}
}
var student1: Student? = Student(name: "Alice")
var teacher1: Teacher? = Teacher(name: "Mr. Smith")
student1?.teacher = teacher1
teacher1?.student = student1
student1 = nil
teacher1 = nil
Output (Objects successfully deallocated)
Mr. Smith is being deallocated
3. Unowned References (unowned Keyword)
An unowned reference works like weak, but it must always have a value and cannot be nil
. It is
used when the referenced object has the same or longer lifetime than the object holding the reference.
Example Using unowned to Prevent Retain Cycle
class CreditCard {
let number: String
unowned let holder: Person // Unowned reference to prevent retain
cycle
init(number: String,
holder: Person) {
self.number = number
self.holder = holder
}
deinit {
print("Credit card \(number) is being deallocated")
}
}
class Person {
let name: String
var card: CreditCard?
init(name: String)
{
self.name = name
}
deinit {
print("\(name)
is being deallocated")
}
}
var person: Person? = Person(name: "Mark")
person?.card = CreditCard(number: "1234-5678-9101-1121", holder: person!)
person = nil // Both objects will
be deallocated
Output (Objects successfully deallocated)
Mark is being deallocated
Best Practices for Using ARC in Swift
- Use weak references when an object can be
nil
to prevent retain cycles. - Use unowned references when an object is guaranteed to exist throughout its lifetime.
- Avoid unnecessary strong references between objects.
- Use optionals (
?
) for weak references to prevent crashes due to early deallocation. - Monitor memory usage using Xcode’s Memory Graph Debugger to detect strong reference cycles.
Common Mistakes in Swift ARC
❌ Using strong references inside closures without [weak self]
, leading to memory leaks.
✅ Always use [weak self]
or [unowned self]
inside closures.
class DataFetcher {
var completion: (() -> Void)?
func fetchData() {
completion = { [weak self] in
print("Data fetched
successfully")
}
}
}