Swift 5: Memory Management

It would be great if we as devs got to play with limitless memory and never had to care about working with it judiciously. Unfortunately, that isn’t true, and hence, we have to behave like a renter to the OS—rent the memory for a while, use the memory, and then hand it back.

Swift is a smart language, and it knows that many devs don’t like handing the memory back to the environment; hence, it keeps track of the allocated memory using a mechanism called ARC (automatic reference counting).

In ARC, if a reference count to a memory location drops to zero, the memory is de-initialised and is then free for reuse.

Retain cycle

ARC vs Garbage collection(GC)

Many programming languages use garbage collection to collect unused memory, whereas swift uses ARC.

ARC is technically a form of Garbage collection. However, while talking about garbage collection, we’re referring to a separate process that runs independent of an application. As such, memory deallocation cannot be predicted with GC. Also, under low memory conditions, garbage collection might halt thread execution, which requires a lot more resources to run smoothly.

Garbage collection is a background process that runs at indeterminate times during the lifetime of the application (usually during idle time) in two steps. In step 1, all the objects that are considered “safe to collect” are marked and in step 2 these objects are deallocated and marked to be collected.

ARC runs part of the application code, and hence it’s very deterministic. Objects get deallocated as soon as they are not required. In contrast with GC, though, ARC is unable to detect reference cycles. Therefore, ARC requires developers to understand the relationship between reference objects in the system and prevent making ownership/reference cycles.

Avoiding Reference Cycles

The main thing a developer needs to do is prevent the reference cycles in ownership. When we reference an object, the default implementation that comes to mind is a strong reference. To monitor retain cycles, we need to watch out for parent-child relationships and make sure that a child doesn’t reference the parent “STRONGLY”, and is instead marked as a non-owning relationship i.e. either weak or unowned.

Reference Cycles

In this section, we’ll see how to use weak references to break reference cycles in the run time object graph. First, we’ll create a retain cycle in our code and then break that retain cycle by making use of weak references.

We have an Employee class as follows:

We can see that when an Employee is initialised, we’ll print the message Employee (employee name) initialised 🥳, and when the same employee is de-initialised, we’ll print Employee (name) de-initialised 💀 so that we can observe the lifetime of the Employee objects.

Let’s subclass the Employee class to create a Manager class and a Worker class as shown below:

Once we have both these classes, we create one Manager object manager, three Worker objects— employee1employee2employee3 —and then we assign respective manager and reports properties, as shown in the code snippet.

The do statement makes a scope and the scope exits once the do statement finishes.

Can you predict what will be printed if we run our playground now?

Retain Cycle

The execution results will print the following on our Xcode console:

We can see that all four objects are initialised as expected, but none of them were de-initialised, even as we went out of scope. Why was that?

To answer this question, let’s have a look at the memory graph for these four objects that got initialised in scope:

As you can see, manager holds a strong reference to its employees, and in return each employee holds a strong reference to the manager. Hence, when manager goes out of scope, its reference count is 3, so it can’t be de-initialised. Similarly, each employee has a reference count of 1, so none of them will be de-initialised, and we have a memory leak.

The next revolution in mobile development? Machine learning. Don’t miss out on the latest from this emerging intersection. Sign up for weekly updates from our crew of mobile devs and machine learners.

“Weak” to the rescue

We can fix the problem by marking the manager relationship in the Worker classes as weak.

Weak is always used on a variable or a var property and is always used on an optional.

The property marked with weak is always optional, so that when the non-owned object goes away, the optional property will automatically switch to nil. So all we need to do is safely handle the optional type and our work is done 🙂

Let’s fix code in the example above.

All we needed to do was to add the keyword weak to the optional manager property in the Worker class.

Because a weak reference does not keep a strong hold on the instance it refers to, it’s possible for that instance to be deallocated while the weak reference is still referring to it.

Please keep in mind that if you set any property observers on weak properties, they won’t be called when ARC sets that reference to nil.

Retain cycle broken

The execution result will now print the following on our Xcode console:

All the objects that were initialised are now de-initialised as expected once they’re out of scope. If the reference to the manager goes away, nothing owns it anymore, and hence the manager is de-initialised. And since the reference from the reports goes away when the manager is deallocated, all the reports go away, too. Now the object graph looks as follows:

In upcoming article we will go through unowned references and closures.

For other updates you can follow me on Twitter on my twitter handle @NavRudraSambyal

Thanks for reading, please share it if you found it useful 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s