José Solórzano

5 min read

Jan 1, 2017

The larger your app becomes and the more code you add to it, the easier it becomes to lose track of how objects in your project are storing references to each other. This is true even when enjoying the benefits of ARC.

If you’re not careful, many of these (strong) references may be causing retain cycles to occur. Before we dive much further into this, let’s begin with some definitions.

What is ARC?

ARC — is a compile-time feature that stands for Automatic Reference Counting. The way ARC works is that the memory for an object is only freed when their reference count reaches zero.

References and object lifecycle

In Swift, all references are strong by default. If you have an object that is being strongly referenced by 10 other objects that means there are 10 references that must be eliminated before that object can be de-allocated.

Remember: an object won’t be de-alloacted as long as there’s at least one strong reference to it.

Never forget — objects won’t be deallocated if they have at least one strong reference to it.

What is a Retain Cycle? — STRONG

Even though ARC does a great job at doing that it is supposed to do — there are some scenarios where it needs an extra pat in the back — the most common being when two objects hold strong references to each other thus preventing their reference count from ever being zero.

Allow me to illustrate:

class MessyClass {var messyInstance : MessyClass? = nil}var messyInstance1: MessyClass? = MessyClass()
var messyInstance2: MessyClass? = MessyClass()
messyInstance1?.messyInstance = messyInstance2
messyInstance2?.messyInstance = messyInstance1

In this code, I create two instances of a class, and I make both instances point to each other. Refer to the graphic representation below:

And now, even if I set them to nil, they won’t be de-allocated.

messyInstance2 = nil
messyInstance1 = nil

Why? — Because even though we cleared ONE reference to each instance, there’s still another one pointing to each other.

WEAK and UNOWNED save the day

Very well then, how do we stop these retain cycles from occurring? We certainly can’t write a whole app without object references — at least not one that does anything useful.

Enter the WEAK keyword.

An object reference created using the WEAK keyword does NOT increase the reference retain count by 1. In addition, weak references zero out the pointer to your object when it successfully deallocates.

Because a weak reference might be pointing to an object that has already been de-allocated (nil), all weak references have to be non-constant (var, not let) optionals.

WEAK in action — Delegates and their Delegatees

One very common scenario for retain cycles are Delegates.

Get José Solórzano’s stories in your inbox

Join Medium for free to get updates from this writer.

Often times we have a controller with a child controller. The child controller creates a reference to its parent in order to notify it when certain events occur.

class ParentController: UIViewController, ChildControllerProtocol {let childController = ChildController()func viewDidLoad() {
childController.delegate = self
}
}protocol ChildControllerProtocol: class {}class ChildController: UIViewController {
var delegate: ChildControllerProtocol?
}

We can clearly see how the child controller holds a strong reference to its parent — causing a retain cycle and a memory leak.

The fix for this retain cycle is a one-liner:

weak var delegate: ChildControllerProtocol?

By using the weak keyword we are now preventing a retain cycle by not increasing the delegate’s retain count by 1, and we are also telling ARC to zero-out the pointer to object once it’s de-allocated.

Using weak references to delegates is encouraged by Apple engineers, let’s take a look at the definitions for UITableViewDelegate and DataSource:

weak public var dataSource: UITableViewDataSource?
weak public var delegate: UITableViewDelegate?

Even more common: Closures

class CarClass {var valves = 5
var engineClosure : (() -> Void)?
init() { engineClosure = {
print(self.valves)
}
}
}
var car = CarClass()

Here we have an even more common scenario: The object holds a strong reference to the closure, and the closure holds a strong reference to the object (via self.valves)

Swift provides an elegant solution to this problem, known as a closure capture list.

Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self) or a variable initialized with some value. These pairings are written using the swift array syntax.

engineBlock = { [weak self] inprint(self.valves)}

It is important to note that not all closures cause retain cycles, it only happens when you are holding a strong reference to the block.

The odd “UNOWNED” and its uses

More often that not, we see the list for a closure that captures “self” declared as follows:

[unowned self] in

What is unowned and why is it replacing the weak keyword?

Weak and unowned references are very similar, but their difference is very important: and unowned reference can never be an optional. Also, unowned references do not zero-out the pointer when an object is de-allocated, which may lead to dangling pointers.

For Obj-C geeks: “unowned” maps to “unsafe_unretained”.

When should we use unowned instead of weak? Quoting Apple engineers:

If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.

And:

Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.

Summary

You have read about automatic reference counting, object referencing and retain-cycles. Although you’ll probably need a couple of projects of your own with their own retain cycles in order to master this knowledge, now you have the all the tools necessary.