Memory management is the process of allocating memory for objects when they are needed, and freeing that memory when they are no longer in use.
Swift uses a system called Automatic Reference Counting (ARC) to track and manage your app's memory usage.
In most cases, this means memory management "just works" in Swift without you needing to do anything.
However, to prevent memory leaks, you need to understand how ARC forms relationships between objects.
Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance.
ARC also keeps a count of how many active properties, constants, and variables are currently referring to that instance.
As long as the "Reference Count" is greater than zero, the object remains alive in memory.
When the count drops to exactly zero, ARC safely deletes the object and reclaims the memory.
By default, whenever you assign a class instance to a property or variable, it creates a strong reference.
A strong reference firmly holds onto the instance, guaranteeing it will not be deallocated as long as the strong reference exists.
class Person {
var name: String
init(name: String) { self.name = name }
}
// Reference count is now 1
var user: Person? = Person(name: "Akash")
// We destroy the strong reference. Count drops to 0. Memory is freed!
user = nil
A problem occurs if two class instances hold strong references to each other.
This creates a "Strong Reference Cycle" or a "Retain Cycle".
Because both objects are keeping each other alive, their reference counts can never drop to zero, causing a severe memory leak.
To resolve strong reference cycles, you can declare one of the references as weak.
A weak reference points to an object but does not increase its reference count.
If the object is deallocated by ARC, the weak reference is automatically set to nil. Therefore, weak references must always be Optional variables.
class Employee {
var name: String
// We use weak to prevent a retain cycle with the Company object
weak var company: Company?
init(name: String) { self.name = name }
}
class Company {
var boss: Employee?
}
Similar to weak references, an unowned reference does not keep a strong hold on the instance it refers to.
The difference is that unowned references are used when you know for a fact the referenced object will never be nil once set.
Unlike weak variables, unowned references are not Optionals. If you try to access an unowned reference after the object has been destroyed, your app will crash!
Retain cycles also frequently happen inside Closures, because closures naturally capture (strongly hold onto) any variables they use.
You solve this by defining a "Capture List" at the start of the closure, specifying [weak self] or [unowned self].
What is the mechanism Swift uses to automatically manage memory for class instances?