Swift Memory Management

Swift Memory Management

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.


How ARC Works

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.


Strong References

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.

Strong Reference Example:

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


The Problem: Retain Cycles

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.


Weak References

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.

Using Weak References:

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? }


Unowned References

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!


Closures and Capture Lists

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].


Exercise

What is the mechanism Swift uses to automatically manage memory for class instances?