What Is Automatic Reference Counting (ARC)

Before we can discuss Automatic Reference Counting, ARC for short, you need to understand the difference between value types and reference types.

Value Types and Reference Types

You’re probably already familiar with classes. Most object-oriented programming languages, such as Ruby, PHP, C#, and Java, define classes. Unless you have experience with C or Objective-C, structures may be new to you.

Even though classes and structures are similar, they differ in a number of key aspects. The difference I’d like to focus on today is how they are stored memory.

In this article, I won’t explain in detail what separates classes from structures. The focus of today's article is how classes and structures behave in terms of memory management. While this may sound like an advanced topic, it is not. It's essential that you understand how Swift’s memory management operates and behaves.

Passing by Value

Structures are an integral part of the Swift programming language. Even if you’re new to Swift, chances are you’ve already been using structures. Create a new playground in Xcode and inspect its contents.

Create A Playground in Xcode

This is what a brand new playground looks like in Xcode, a comment at the top, an import statement for the UIKit framework, and a variable declaration.

//: Playground - noun: a place where people can play

import UIKit

var str = "Hello, playground"

The variable str is of type String. In Swift, strings are structures. Don’t believe me? Press Option, click the str variable, and click the String symbol to bring up Xcode’s documentation browser.

Bring Up Xcode's Documentation Browser

Bring Up Xcode's Documentation Browser

As you can see at the top, String is a structure. What does that mean? And why is it important to this discussion? Instances of structures and enumerations are passed by value. That’s interesting, but what does it mean? This is best illustrated with an example.

What Does Passing by Value Mean

Let’s declare a second variable, newStr, and assign the value of str to the new variable.

var str = "Hello, playground"
var newStr = str

The value stored in str is equal to the value stored in newStr. We can prove this with the following if statement.

if str == newStr {
    print("\(str) is equal to \(newStr)")
} else {
    print("\(str) is NOT equal to \(newStr)")
}

But what happens if we assign a new value to str. Below the if statement, modify the value stored in str.

str = "Hello, world"

What do you expect the value stored in newStr is? Move the if statement you added a moment ago below the assignment of str. This is what the playground should look like.

//: Playground - noun: a place where people can play

import UIKit

var str = "Hello, playground"
var newStr = str

str = "Hello, world"

if str == newStr {
    print("\(str) is equal to \(newStr)")
} else {
    print("\(str) is NOT equal to \(newStr)")
}

Are you surprised by the result you see in the results panel on the right? I repeat what I wrote earlier. Instances of structures and enumerations are passed by value. Let me explain what this means using the playground.

Remember that we stored ”Hello, playground" in the variable str. We then assigned the value of str to newStr. Under the hood, the value of str is copied and stored in newStr. This means that separate values are stored in str and newStr. The values are equal, but, in memory, they are different. That’s what it means to pass by value. When the value of str was assigned to newStr, it was passed by value.

Passing by Reference

Don’t worry if it doesn’t click yet. The next example should fill in any remaining gaps. Clear the playground and define a class named Person. Don’t worry about the syntax if you’re new to Swift.

class Person {

    var first: String
    var last: String

    init(first: String, last: String) {
        self.first = first
        self.last = last
    }

}

The Person class defines two variable properties of type String, first and last. We also define an initializer that accepts two arguments of type String.

Let’s create an instance of the Person class and output the values stored in its first and last properties.

let jim = Person(first: "Jim", last: "Simmons")
jim.first   // "Jim"
jim.last    // "Simmons"

We now declare a second constant, jane, and assign the value stored in jim to jane. It should be no surprise that the values stored in the first and last properties of jane are equal to those of jim.

let jane = jim
jane.first  // "Jim"
jane.last   // "Simmons"

But observe what happens if we modify the first name of Jane. Are you surprised by the result?

jane.first = "Jane"

jim.first   // "Jane"
jane.first  // "Jane"

The value stored in the first property of jim is also modified. Why is that? Instances of classes are passed by reference. When we assigned jim to jane, the value stored in jim was not copied. Instead, the reference of the Person instance stored in jim was assigned to jane. This means that jim and jane are pointing to the same Person instance. Only one instance of the Person class is present in memory.

Memory Management

You may be wondering why it’s important to know and understand the differences between value types and reference types. Anyone working with Swift should know about the differences between value and reference types. But the difference is especially important in the context of memory management.

Let's start with some good news. You don’t need to worry about the memory management of value types, structures and enumerations. The compiler is in charge of the memory management of structs and enums. Because they are passed by value, the compiler knows and understand when to dispose of instances of structs and enums.

This isn’t true for class instances, though. Most of the time, you don’t need to worry about the memory management of class instances. While this is good news, it doesn’t mean that you shouldn’t need to know about memory management. Let me explain why that is.

Automatic Reference Counting (ARC)

It's essential that an object, an instance of a class, structure, or enumeration, is deallocated when it's no longer needed. When an object is deallocated, the memory occupied by the object is freed. This is instrumental for the performance and efficiency of the operating system.

Because class instances are passed by reference, it's possible for properties, constants, and variables to keep a reference to the same class instance. This is a key aspect of object-oriented programming. But this makes it difficult for the compiler to know and understand when it's safe to deallocate a class instance.

To make the compiler's task easier, Swift uses Automatic Reference Counting, ARC for short. Apple first introduced Automatic Reference Counting in Mac OS X Snow Leopard and iOS 4. A more mature version of ARC was made available in Mac OS X Lion and iOS 5. This was a very important change for the developer community.

The idea underlying ARC is simple. It keeps track of class instances and it decides when it's safe to deallocate the class instances it monitors. It does this by counting the references of each class instance. Let me illustrate this with an example.

In this example, we define a class, Device. The class has two properties of type String, model and manufacturer. We can initialize an instance of the Device class by invoking the init(model:manufacturer:) initializer.

class Device {

    let model: String
    let manufacturer: String

    init(model: String, manufacturer: String) {
        self.model = model
        self.manufacturer = manufacturer
    }

}

Before we create an instance of the Device class, we declare two variables of type Device?.

var deviceJim: Device?
var deviceJane: Device?

We then create an instance of the Device class and assign it to deviceJim. Because the deviceJim variable holds a reference to the Device instance we created, the Device instance isn't deallocated.

deviceJim = Device(model: "iPhone 7", manufacturer: "Apple")

Automatic Reference Counting ensures a class instance isn't deallocated as long as a property, constant, or variable keeps a strong reference to the class instance. Strong reference? We discuss strong references later.

As the name implies, Automatic Reference Counting keeps count of the number of references to a class instance. It does this to understand when it's safe to dispose of a class instance.

We can store another reference to the Device instance we created in the deviceJane variable. Both deviceJim and deviceJane point to the same Device instance. Only one Device instance is currently in memory.

deviceJane = deviceJim

We can decrement the reference count of the Device instance by setting deviceJim to nil. But because deviceJane continues to hold a reference to the Device instance, it isn't deallocated. This also proves that deviceJim and deviceJane were pointing to the same Device instance.

deviceJim = nil

The moment we set deviceJane to nil, no properties, constants, or variables keep a reference to the Device instance. As a result, the Device instance is deallocated by Automatic Reference Counting. This also means that the memory taken up by the Device instance is freed. That is the goal of Automatic Reference Counting.

You can verify this by adding a deinitializer to the Device class. The deinitializer function of a class is invoked immediately before an instance of the class is deallocated. It gives the instance an opportunity to clean up any resources it has used during its lifetime.

class Device {

    let model: String
    let manufacturer: String

    init(model: String, manufacturer: String) {
        self.model = model
        self.manufacturer = manufacturer
    }

    deinit {
        print("The device is about to be deallocated.")
    }

}

In the deinit function, we add a print statement that informs us the class instance is about to be deallocated. If we don't set the deviceJane variable to nil, the deinitializer isn't invoked. This means the class instance is not deallocated.

What Is Automatic Reference Counting

If we set deviceJane to nil, no properties, constants, or variables have a hold on the Device instance. As a result, the Device instance is deallocated and the deinitializer is invoked.

What Is Automatic Reference Counting

But Automatic Reference Counting Needs Your Help

There are times that Automatic Reference Counting isn't able to figure out when it's safe to deallocate a class instance. To avoid retain cycles and memory leaks, Automatic Reference Counting needs your help. In the next article of this series, we zoom in on retain cycles.

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By

About Bart Jacobs

About bart jacobs

My name is Bart Jacobs and I run a mobile development company, Code Foundry. I've been programming for more than fifteen years, focusing on Cocoa development soon after the introduction of the iPhone in 2007.

Stop Writing Swift That Sucks

In my free book, you learn the four patterns I use in every Swift project I work on. You learn how easy it is to integrate these patterns in any Swift project.