Swift 4.0: Automatic Reference Counting (ARC) — Part 1

Rahul Singh
Swift India
Published in
8 min readApr 22, 2018

Swift too uses Automatic Reference Counting (ARC) to keep track and manage all the objects created by the application. This signifies that it handles Memory Management itself and we do not need to take care of it. But there are few situations where we as a developer need to provide few other information, in particularly, the relationship between objects, to avoid Memory Leaks. We would be discussing those situations here.

Reference Counting applies only to the instances of the Class because it is Reference Type. This means that multiple objects can refer to the same object. Where as Structures and Enumerations are Value Types.

How ARC works?

Every time we create an instance of a Class, ARC allocates memory that holds information about the Type and Value it holds to. Whenever we assign a class instance to a constant or variable, that particluar constant or variable makes a strong reference to the instance and does not allow it to be deallocated as long as that strong reference exists. ARC keeps track of all the constants and variables currently it is referring to and will not deallocate until the last active reference is released. Once, all the active references are removed, ARC frees up the memory by removing the instance. This ensures that class instances do not take up unnecessary memory when not required.

Let’s see with an Example:

class Employee {
let empName: String
init(employeeName empName: String) {
self.empName = empName
print("\(empName) is being initialized")
}

deinit {
print("\(empName) is being deinitialized")
}
}
var emp1: Employee?
var emp2: Employee?
var emp3: Employee?

In the above Class named Employee, we have an initialiser that sets the instance and prints a message to indicate that initialisation is in process. This also has a de-initialiser that prints out a message when an insance of the Class is deallocated.

Now, let’s create Employee instance and assign it to the first variable emp1.

emp1 =  Employee(employeeName: "John Appleseed")
// Prints "John Appleseed is being initialized"

Now that Employee instance is assigned to the emp1. There is a strong reference from emp1 to the new Employee instance and ARC ensures that this Employee is kept in memory and not deallocated. Retain Count has increased by 1.

emp2 = emp1
emp3 = emp1

Now, there are 3 strong references to Employee instance that we created at the beginning. The Retain Count would now be 3.

Now, lets break 2 of these strong references (including the original reference) by assigning nil. The Retain Count would now be 1. Employee instance would not deallocate as there is still exisits 1 last strong reference (emp3).
ARC would not deallocate the Employee instance until the 3rd and final strong reference is broken.

emp1 = nil // Retain Count would decrease by 1
emp2 = nil // Retain Count would decrease by 1

As soon as 3rd and final strong reference is set to nil, no further strong reference exisits. ARC is now free to remove the Employee instance from the Memory. Pretty Simple Huh!!. Lets dive deep in.

emp3 = nil
// Prints "John Appleseed is being deinitialized"

What are Reference Cycles?

There could be a scenario where two class instance holds a strong Reference to each other and there is no way for the system to deallocate them. This means that the Retain Count of both the class insance would never come down to 0. This is known as Strong Refernce Cycle. Let us see this with an example.

class Employee {
let empName: String
var dept: Department?

init(employeeName empName: String) {
self.empName = empName
print("\(empName) is being initialized")
}

deinit {
print("\(empName) is being deinitialized")
}
}
class Department {
let deptName: String
var emp: Employee?
init(departmentName deptName: String) {
self.deptName = deptName
print("\(deptName) is being initialized")
}

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

In the above code, we have created 2 class. One for Employee and other for Department. Both these classes have strong reference to Department(dept) and Employee(emp) instance respectively. The Retain Count for john and iOS variable would be 1.

var john: Employee? = Employee(employeeName: "john")
var iOS: Department? = Department(departmentName: "iOS")
// Prints "john is being initialized"
// Prints
"iOS is being initialized"

We would create an Employee and Department instance. This in turn would create a strong reference to john and iOS variable (Image 1–1).

Image 1–1

Now assign the two instances together so that Employee is associated with Department and Department is linked to Employee. This would create a strong reference cycle among each other and the Retain Count for john and iOS variable would increase by 1. Retain Count for john and iOS variable is 2 (Image 1-2).

Image 1-2
john!.dept = iOS
iOS!.emp = john

If we set the john and iOS variable to nil , there would still exisits a strong reference between the Employee and Department instance which now cannot be broken. Retain Count now would be 1. Since, the Retain Count did not drop to 0, ARC did not deallocate the instances. Such scenarios causes memory leaks in our application (Image 1-3).

Image 1-3

How to Resolve these Reference Cycles?

Swift provides two ways of resolving these Strong Reference Cycles: weak and unowned reference.

Weak Reference

Apple Doc states that a weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. This behavior prevents the reference from becoming part of a strong reference cycle. You indicate a weak reference by placing the weak keyword before a property or variable declaration.

Weak Reference should be used in cases where other instance can be deallocated first without any issue. Confused!!! Let us see this with an example.

Just add weak before emp variable in the Deparment Class. The class would look like the below.

class Department {
let deptName: String
weak var emp: Employee?
init(departmentName deptName: String) {
self.deptName = deptName
print("\(deptName) is being initialized")
}

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

Now, if we create instance variable of Employee and Deparment and assign these instances to each other, Employee instance would hold a strong reference to the Deparment but Department insatance would hold a weak reference to the Employee instance. Retain Count of Employee would be 1 where as Retain Count of Department would be 2 (Image 1-4).

var john: Employee? = Employee(employeeName: "john")
var iOS: Department? = Department(departmentName: "iOS")
john!.dept = iOS
iOS!.emp = john
Image 1-4

Since Employee instance has only one strong reference, as soon as we set the Employee instance to nil, the Retain Count would become 0 and it would be deallocated. Also, the emp variable of the Department would be set to nil reducing the Retain Count to 1(Image 1-5).

john = nil
// Prints "john is being deinitialized"
Image 1-5

Now, the only strong Reference left to the Deparment is from the iOS variable. Setting nil to the iOS variable would reduce the Retain Count by 1, thus making it 0. Hence, ARC would deallocate the Department instance (Image 1-6).

iOS = nil
// Prints "iOS is being deinitialized"
Image 1-6

Unowned Reference

Unowned reference too does not keep a strong hold on the instance it refers to. However, there is one major difference between Week and Unowned Reference. It should only be used when we are sure that both the instance has almost same or more life span. A Weak references must be defined using optional whereas Unowned references must be defined using non-optional types. Both do not increase the Retain Count. Lets see an example.

Suppose we are creating an application for a car Company where they need to keep track of every vehicle sold to the People. We would have Vehicle and Driver Model class.

In the following we have created 2 Class. Owner and Vehicle class. In this Owner may or may not Vehicle but Vehicle to come on Road would need to have a Owner . In such case, the Owner should hold a strong reference to the Vechicle but the Vehicle should just have a unowned reference to the Owner. This is because there is noVehicle which doen’t have an Owner . So, its existence only lies till the Owner owns the Vehicle. But the same doesn’t hold true for Owner.

class Owner {
let ownerName: String
var vehicle: Vehicle?

init(ownerName name: String) {
self.ownerName = name
print("\(ownerName) is being initialized")
}
deinit {
print("\(ownerName) is being deinitialized")
}
}
class Vehicle {
let vehicleNumber: String
unowned let owner: Owner
init(vehicleNumber number: String, owner: Owner) {
self.vehicleNumber = number
self.owner = owner
print("\(vehicleNumber) is being initialized")
}
deinit {
print("\(vehicleNumber) is being deinitialized")
}
}

Now, lets create the Owner and Vehicle instance and associate with each other. The Retain Count for the Owner instance is 1 which is held by variable. In case of Vehicle, its just the Owner who is holding the strong reference to it (Image 2–1).

var appleseed: Owner?
appleseed = Owner(ownerName: "John Appleseed")
appleseed?.vehicle = Vehicle(vehicleNumber: "TE 12 MP 5678", owner: appleseed!)// Prints "John Appleseed is being initialized"
// Prints "TE 12 MP 5678 is being initialized"
Image 2–1

Its existence is only till the existence of the Owner. Once, the Owner instance is deallocated, the Vehicle instance would automatically be deallocated as there is no strong reference associated to it (Image 2–2, Image2–3).

appleseed = nil
// Prints "John Appleseed is being deinitialized"
// Prints "TE 12 MP 5678 is being deinitialized"
Image 2–2
Image2–3

Source: Apple Docs and of course Internet 😄

We’ve covered the basics of ARC in this tutorial. You can find the Playground here. Feel free to download and play around with it.

Part 2 of this series can be found here.

Do write your comments, if you enjoyed this post.

I’d like to grow my readership. Can you help me out by sharing this blog post?

LinkedIn Twitter Facebook

--

--