Easy Tutorial
❮ Swift Loops Swift Basic Syntax ❯

Swift Automatic Reference Counting (ARC)

Swift uses Automatic Reference Counting (ARC) to track and manage your application's memory usage.

Typically, we do not need to manually release memory because ARC automatically frees up the memory used by class instances when those instances are no longer needed.

However, there are times when we still need to manage memory in our code.

ARC Features

-

Whenever a new instance of a class is created using the init() method, ARC allocates a chunk of memory to store information about that instance.

-

This memory holds the instance's type information and the values of all its properties.

-

When the instance is no longer needed, ARC frees up the memory it occupied, allowing the memory to be used for other purposes.

-

To ensure that active instances are not destroyed, ARC tracks and counts how many properties, constants, and variables are currently referencing each instance.

-

Assigning an instance to a property, constant, or variable creates a strong reference to that instance. As long as there is a strong reference, the instance is not allowed to be destroyed.

ARC Example

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

// Values are automatically initialized to nil, and no reference to a Person instance yet
var reference1: Person?
var reference2: Person?
var reference3: Person?

// Create a new instance of Person
reference1 = Person(name: "tutorialpro")

// Assign to other two variables, creating two more strong references
reference2 = reference1
reference3 = reference1

// Break the first strong reference
reference1 = nil
// Break the second strong reference
reference2 = nil
// Break the third strong reference and call the deinitializer
reference3 = nil

The output of the above program is:

tutorialpro is being initialized
tutorialpro is being deinitialized

Strong Reference Cycles Between Class Instances

In the example above, ARC tracks the number of references to the new Person instance you create and deallocates it when it is no longer needed.

However, it is possible to write code where a class instance never gets to a point where it has zero strong references. This happens when two class instances hold a strong reference to each other, causing each other to not be destroyed. This is known as a strong reference cycle.

Example

The following example demonstrates an unintended strong reference cycle that occurs between two class instances. It defines two classes: Person and Apartment, which model a building and its residents:

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

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { print("Apartment #\(number) is being deinitialized") }
}

// Both variables are initialized to nil
var tutorialpro: Person?
var number73: Apartment?

// Assign values
tutorialpro = Person(name: "tutorialpro")
number73 = Apartment(number: 73)

// The exclamation marks are used to unwrap and access the instances inside the optional variables tutorialpro and number73
// A strong reference cycle is created
tutorialpro!.apartment = number73
number73!.tenant = tutorialpro

// When the strong references from the tutorialpro and number73 variables are broken, the reference count does not drop to zero, and the instances are not deallocated by ARC
// Note that no deinitializers are called when you set these two variables to nil
// The strong reference cycle prevents the deallocation of the Person and Apartment instances, causing a memory leak in your application
tutorialpro = nil
number73 = nil

Resolving Strong Reference Cycles Between Class Instances

Swift provides two ways to resolve strong reference cycles when you work with properties of class type:

Weak and unowned references allow one instance in a cycle to reference the other without keeping a strong hold on it. This allows instances to reference each other without creating a strong reference cycle. Use weak references for instances that will become nil during their lifecycle. Conversely, use unowned references for instances that will never be assigned nil after initialization.

Weak Reference Example

class Module {
    let name: String
    init(name: String) { self.name = name }
    var sub: SubModule?
    deinit { print("\(name) main module") }
}

class SubModule {
    let number: Int

    init(number: Int) { self.number = number }

    weak var topic: Module?

    deinit { print("Submodule topic number is \(number)") }
}

var toc: Module?
var list: SubModule?
toc = Module(name: "ARC")
list = SubModule(number: 4)
toc!.sub = list
list!.topic = toc

toc = nil
list = nil

The output of the above program is:

ARC main module
Submodule topic number is 4

Unowned Reference Example

class Student {
    let name: String
    var section: Marks?

    init(name: String) {
        self.name = name
    }

    deinit { print("\(name)") }
}
class Marks {
    let marks: Int
    unowned let stname: Student

    init(marks: Int, stname: Student) {
        self.marks = marks
        self.stname = stname
    }

    deinit { print("Student's score is \(marks)") }
}

var module: Student?
module = Student(name: "ARC")
module!.section = Marks(marks: 98, stname: module!)
module = nil

The output of the above program is:

ARC
Student's score is 98

Strong Reference Cycles for Closures

Strong reference cycles can also occur when you assign a closure to a property of a class instance, and the body of that closure captures the instance. This can happen if the closure accesses an instance property, such as self.someProperty, or calls an instance method, such as self.someMethod. Both of these actions capture self and create a strong reference cycle.

Example

The following example demonstrates how a strong reference cycle is created when a closure captures self. It defines a class called HTMLElement, which represents a single element in HTML:

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

// Create an instance and print the information
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

The HTMLElement class creates a strong reference cycle between the instance and the closure assigned to asHTML. The asHTML property of the instance holds a strong reference to the closure. However, the closure uses self within its body (referencing self.name and self.text), thus the closure captures self, which means the closure in turn holds a strong reference to the HTMLElement instance. This creates a strong reference cycle between the two objects.

To resolve the strong reference cycle caused by closures: Define a capture list as part of the closure's definition to break the strong reference cycle between the closure and the class instance.


Weak and Unowned References

When a closure and the captured instance always reference each other and are always deallocated together, define the capture inside the closure as an unowned reference.

Conversely, when the captured reference might sometimes be nil, define the capture inside the closure as a weak reference.

If the captured reference will never be set to nil, it should be an unowned reference instead of a weak one.

Example

In the previous HTMLElement example, using an unowned reference is the correct way to resolve the strong reference cycle. This is how you can write the HTMLElement class to avoid the strong reference cycle:

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

// Create and print an HTMLElement instance
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

// The HTMLElement instance will be deallocated, and you will see its deinitializer print a message
paragraph = nil

The output of the above program is:

<p>hello, world</p>
p is being deinitialized
❮ Swift Loops Swift Basic Syntax ❯