Memory Leaks in Swift

Unit Testing and other tools to avoid them.

Leandro Pérez
May 7, 2018 · 9 min read

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

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

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?

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

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?

  1. 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.
  2. 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.
  3. 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.
  4. 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

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

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

You can create a set of tests that will instantiate objects and try things on them. You define what to expect and if the results are as expected, the test will pass, green. If the outcomes are not what you defined as expected, the test fails in red.

Testing for leaks in initialization

Testing initialization

Testing for Leaks in View Controllers

Testing init + viewDidLoad on a view controller

Using SpecLeaks you don’t need to manually call view on the view controller in order for viewDidLoad to be called. SpecLeaks will do that for you when you are testing a UIViewController subclass.

Testing for Leaks when a method is called

Test if CustomViewController leaks when doSomething is called

To wrap it up

Unit Testing will not guarantee the absence of leaks though. You will never cover all the permutations of method calls and states. Testing the whole spectrum of interactions with an object might be just impossible. Also, it is often necessary to mock dependencies. And the original dependencies might be the ones leaking.

Unit Testing will decrease the chances of leaking. It is quite easy to test and to spot leaks in closures with SpeakLeaks. Like flatMap or any other escaping closure that retains self. The same if you forget to declare a delegate as weak.

I use RxSwift a lot, and flatMap, map, subscribe and others require to pass closures. For such cases, the absence of weak/unowned often will create leaks that can be detected with SpecLeaks quite easily.

Personally, I am trying to add this kind of tests to all my classes. Whenever I create a view controller, for example, I just add a Spec for it. Sometimes view controllers contain leaks in view loading, and those can be quickly caught with this kind of testing.

What do you think? Do you write unit tests for memory leaks? Do you write tests at all?

I hope you enjoy reading this article, send me a line with your opinion or questions! And feel free to try SpeakLeaks :)

Archived — Flawless iOS

Archive of Flawless iOS publication — no longer accepting submissions

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store