Strong, weak, and unowned references — Swift 5 (iOS)

Different referencing types in Swift

Ayush Singhi
5 min readApr 25, 2021

Hello there!

I hope you are doing well! In this article, we shall take a look at how object references are declared in the Swift programming language and what are the different types of referencing techniques that can be used, and how.

Referencing in Swift

In Swift, only classes are declared as referencetype, whereas structs and enums are both value types. So when classes are instantiated, ARC reserves a memory block to store its details and uses this address for reference.
According to Swift’s official documentation:

Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. This memory holds information about the type of the instance, together with the values of any stored properties associated with that instance.

On the other hand, no such references are allocated for structures and enums. They are passed as copies of values. According to Swift’s documentation:

Structures and enumerations are value types, not reference types, and aren’t stored and passed by reference.

Types of references

Swift supports three types of references —

  1. strong
  2. weak
  3. unowned

Strong reference —

Whenever we instantiate a class and assign it to a variable or a constant, a strong reference is created. This reference keeps a stronghold of the instance and does not allow ARC to deallocate it. Thus, keeping it alive. This makes strong very useful for development.

But even though, strong might seem useful and robust, it creates a problem of Strong Retain Cycles which are cyclic graphs like structures formed due to interdependency between references.
Let’s consider an example involving two classes — Car and Owner, where an owner may have a car and similarly a car may have an owner.

Car and Owner classes
Driver Code -var owner: Owner? = Owner(name: "Ayush") // An owner
var car: Car? = Car(brand: "Tata") // A car
owner?.car = car
car?.owner = owner
Variables owner and car hold strong references to Owner and Car instances respectively. Also, both car and owner hold a strong reference to each other.

It is evident from the above example that Owner holds a strong reference to a Car instance, while Car holds a strong reference to an Owner instance, hence, creating an internal retain cycle. Let’s see what happens if we try to solve this by breaking the references and assigning nil to the variables.

owner = nil // Break the reference
car = nil // Break the reference
Strong retain cycle between instance User and instance Address

What we see is that even when the references to Owner and Car instances are broken, the internal retain cycle is not removed and it restricts ARC from deallocating the instances. As a result, they continue to live. The problem here is that this type of memory retention might lead to memory leaks in the application. In order to solve this, Swift developers came up with— weak and unowned.

Weak reference —

Weak belongs to a different category of references that allows ARC to deallocate an instance even when active references(not strong) exist. So unlike strong, weak references do not restrict ARC from doing its job. Whenever ARC deallocates an instance, it automatically assigns a nil value to all its weak references. Now, since ARC can update the value of weak references at runtime, they must always be declared as a var and optional.

In the above example of Car and Owner, we saw that a retain cycle was created because of strong references. So let's modify the Owner class a bit.
Since the owner may or may not have a car, we will make car an optional weak variable.

// In class Owner, replace "var car: Car?" withweak var car: Car? // Making car weak

In the above example, if we now try to break the reference of car by assigning it nil, the owner does not try to retain the instance because it has a weak reference.

var car = nil

In this case, no retain cycle is created as car is a weak reference and hence its instance is deallocated, which is evident from the output on the console — the deinit statement of class Car.

Unowned reference —

Unowned is the third type of reference. Similar to weak , it also allows ARC to deallocate an instance if it has no active strong references to it. But unlike weak, an unowned variable cannot hold nil value which is why ARC does not assign it nil at runtime.

However, an unowned reference should be used only when one is sure that the other instance will always outlive or has a longer lifetime. Since the unowned instance always outlives the containing instance, it is always expected to have a value. According to Swift’s documentation —

If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.

Let’s consider a different example of two entities — Car and Engine. An engine is always attached to a Car and cannot outlive it.

Notice Engine holds an unowned reference to Car
Driver Code -var car: Car? = Car(brand: "Tata") // A car
car.engine = Engine(type: "v8", car: car!)

Here, var car holds a strong reference to a car, which holds an instance to an Engine object. That contains an unowned car reference, as engine cannot outlive car itself. Hence, no internal retain cycle is formed. If we now destroy the car reference, the engine will be automatically destroyed and memory will be reclaimed by ARC.

var car = nil

As soon as the above statement is executed, the deinit is executed and prints

Car deallocated
Engine deallocated
Instance destroyed as the reference to the car is broken.

Thus, we see what the uses and the objectives of the three types of references in Swift are and how they differ from each other.

Thank you for following along. I hope this article has been helpful.

Bye for now! 👋
Happy Coding 💻

--

--