In this article we will talk about memory leaks and we will learn how to use Unit Testing to detect them. Here’s a sneak peek:

This is a test written in SpecLeaks.

Important: I will explain what memory leaks are, talk about retain cycles and other things you might already know. If you want to read only about Unit Testing Leaks, skip to the last section.

Memory Leaks

Indeed, it’s one of the most frequent problems we face as developers. We code feature after feature and as the app grows, we introduce leaks.

A memory leak is a portion of memory that is occupied forever and never used again. It is garbage that takes space and causes problems.

Memory that was allocated at some point, but was never released and is no longer referenced by your app. Since there are no references to it, there’s now no way to release it and the memory can’t be used again. Apple Docs

We all create leaks at some point, from junior to senior devs. It doesn’t matter how experienced we are. It is paramount to eliminate them to have a clean, crash-free application. Why? Because they are dangerous.

Leaks are dangerous

Not only they increase the memory footprint of the app, but they also introduce unwanted side effects and crashes.

Why does the memory footprint grow? It is a direct consequence of objects not being released. Those objects are actually garbage. As the actions that create those objects are repeated, the occupied memory will grow. Too much garbage! This can lead to memory warnings situations and in the end, the app will crash.

Explaining unwanted side effects requires a little more detail.

Imagine an object that starts listening to a notification when it is created, inside init . It reacts to it, saving things to a database, playing a video, or posting events to an analytics engine. Since the object needs to be balanced, we make it stop listening to the notification when it is released, inside deinit .

What happens if such an object leaks?

It will never die and it will never stop listening to the notification. Each time the notification is posted, the object will react to it. If the user repeats an action that creates the object in question, there will be multiple instances alive. All those instances responding to the notification and stepping into each other.

In such situations, a crash might be the best thing that happens.

Multiple leaked objects reacting to app notifications, altering the database, the UI, corrupting the entire state of the app. You can read about the importance of kind of problems in “Dead programs tell no lies” in The Pragmatic Programmer.

Leaks will undoubtedly lead to a bad UX and poor ratings in the App Store.

Where do Leaks come from?

Leaks may come from a 3rd party SDK or framework for example. Even from classes created by Apple too like CALayer or UILabel . In those cases there isn’t much we can do, except waiting for an update or discarding the SDK.

But it is much more likely that leaks are introduced by us in our code. The number one reason for leaks are retain cycles.

In order to avoid leaks, we must understand memory management and retain cycles.

Retain Cycles

The word retain comes from the Manual Reference Counting days in Objective-C. Before ARC and Swift and all the nice things we can do now with value types, there was Objective-C and MRC. You can read about MRC and ARC in this article.

Back in those days we needed to know a bit more about memory handling. Understanding the meaning of alloc, copy, retain and to how to balance those actions with opposites, like release, was crucial. The basic rule was: whenever you create an object, you own it and you are responsible for releasing it.

Now things are much easier, but still, there are some concepts that need to be learned.

In Swift, when an object has a strong association to another object, it is retaining it. When I say object I am talking about Reference Types, Classes basically.

Struct and Enums are Value Types. It is not possible to create retain cycles with value types only. When capturing and storing value types (structs and enums), there is no such thing as references. Values are copied, rather than referenced, although values can hold references to objects.

When an object references a second one, it owns it. The second object will stay alive until it is released. This is known as a Strong Reference. Only when you set the property to nil will the second object be destroyed.

Strong association

If A retains B and B retains A there is a retain cycle.

A 👉 B + A 👈 B = 🌀

A retain cycle

In this example, it would be impossible to dealloc neither the client nor the server.

In order to be released from memory, an object must first release all its dependencies. Since the object itself is a dependency, it cannot be released. Again, when an object has a retain cycle, it cannot die.

Retain cycles are broken when one of the references in the cycle is weak or unowned. The cycle must exist because it is required by the nature of the associations we are coding. The problem is that all the associations cannot be strong. One of them must be weak.

A weak reference breaks the retain cycle.

How to break retain cycles

Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references. Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle. Apple’s Swift Programming Language

Weak: A variable can optionally not take ownership of an object it references to. A weak reference is when a variable does not take ownership of an object. A weak reference can be nil.

Unowned: Like weak references, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is assumed to always have a value. Because of this, an unowned reference is always defined as a non-optional type. An unowned reference cannot be nil.

When to Use Each:

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. Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. Apple’s Swift Programming Language

weak vs unowned references

It is not rare to forget a weak self somewhere along the way while we code. We usually introduce leaks when we write block closures, like flatMap and map inside reactive code, or when we code observers, or delegates. In this article you can read about leaks in closures.

How to eliminate Memory Leaks?

Don’t create them. Have a strong understanding of memory management. Have a strong code-style defined for your project and respect it. If you are tidy and respect your code-style, the absence of weak self will be noticeable. Core reviews can really help. Use Swift Lint. It is a great tool that enforces you to adhere to a code style and keep rule 1. It helps you detect early issues at compile-time. Like delegate variable declarations that are not weak, becoming potential retain cycles. Detect leaks at run-time and make them visible. If you know how many instances of a certain object must be alive at a time, you can use LifetimeTracker. It is a great tool to have running in development mode. Profile the app frequently. The memory analysis tools that come with XCode do an excellent job. See this article. Instruments used to be the way to go not a while ago and it is great tool too. Unit Test Leaks with SpecLeaks. This pod uses Quick and Nimble and lets you easily create tests for leaks. You can read about it in the following section.

Unit Testing Leaks

Once we know how cycles and weak references work, we can write code to test for retain cycles. The idea is to use weak references to probe for cycles. With a weak reference to an object we can test if that object leaks or not.

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. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated.

Let’s say we want to see if object x leaks. We can create a weak reference to it and call it leakReferece. If x is released from memory, ARC will set leakReference to nil. So, if x leaks, the leakReferece can never be nil.

Testing if an object leaks

If x is actually leaking, the weak variable leakReference will point to the leaked instance. On the other hand, if the object is not leaking, after setting it to nil, it should not exist anymore. In that case, leakReference will be nil.

“Swift by Sundell” wrote a detailed explanation of different kinds of leaks in this article. That post was really helpful for me to write this article and SpecLeaks too. Another nice article that follows a similar approach.

Based on this notion, I have created SpecLeaks, an extension of Quick and Nimble that allows you to test for leaks. The idea is to code unit test for leaks without having to write much boilerplate code.

SpecLeaks

Quick and Nimble is a great combination to write unit tests in a more humanly readable fashion. SpecLeaks is only a few additions to those frameworks that will let you create unit tests to see if objects are leaking.

If you don’t know about unit testing, this screenshot might give you a hint of what it does: