Swift Memory Management Example

1. Introduction

Memory management in Swift ensures that instances don’t take up space when they’re no longer needed. Automatic Reference Counting (ARC) is the mechanism by which Swift manages memory. It automatically frees up the memory used by instances which are no longer needed. In this post, we’ll take a look at how ARC works and dive into strong, weak, and unowned references which play a pivotal role in memory management.

2. Source Code Example

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized.")
    }
    var apartment: Apartment?
    deinit {
        print("\(name) is being deinitialized.")
    }
}

class Apartment {
    let unit: String
    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) is being initialized.")
    }
    weak var tenant: Person?
    deinit {
        print("Apartment \(unit) is being deinitialized.")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil
unit4A = nil

Output:

John Appleseed is being initialized.
Apartment 4A is being initialized.
John Appleseed is being deinitialized.
Apartment 4A is being deinitialized.

3. Step By Step Explanation

1. Initialization and Deinitialization:

- We defined two classes, Person and Apartment, both of which print a message upon initialization (init) and deinitialization (deinit).

- When we create a new instance of a class using its initializer, we see the initialization message. When the instance is no longer needed and is removed from memory, we see the deinitialization message.

2. Setting up Relationships:

- The Person class has an optional apartment property, and the Apartment class has an optional tenant property.- We then create an instance of Person named john and an instance of an Apartment named unit4A. Both classes print their respective initialization messages.

3. Circular Reference Issue:

- We set john's apartment property to unit4A and unit4A's tenant property to john, creating a strong reference cycle.

- In languages without automatic memory management, this would result in a memory leak because neither instance can be deallocated due to the strong references they hold to each other.

4. Using weak to Prevent Memory Leaks:

- In the Apartment class, the tenant property is defined as a weak reference. This means it doesn't keep a strong hold on the Person instance it refers to.

- When we set both john and unit4A to nil, this breaks the reference cycle. 

  • The deinit code for both instances is called, and both are deallocated from memory, indicating that there's no memory leak. It's worth noting that weak references are always optional since they can be set to nil automatically by ARC when the instance it refers to is deallocated. 
  • Another keyword, unowned, can also be used to prevent strong reference cycles and behaves slightly differently than weak. It's non-optional and doesn’t add safety checks, which means it can lead to runtime crashes if accessed after the instance it refers to is deallocated. 

In conclusion, ARC in Swift works automatically to manage memory, but developers should be mindful of reference cycles. Using weak and unowned appropriately can help in avoiding memory leaks.