3 Different Techniques to Find Memory Leaks in iOS

Hunt memory leaks in iOS like a pro!

Vitor Ferraz Varela
bitso.engineering
5 min readApr 11, 2022

--

Photo by Jeremy Bishop on Unsplash

Introduction

When developing for iOS or any other platform, it is very important to have in mind that your customer expectations are very high for your application to perform very well. They shouldn’t worry if the app responds slowly, the interface is sluggish, or any other kind of problem.

Imagine that your customers are paying their bills and suddenly the app crashes. That is not the experience they are expecting and in this case, it’s even worse because they couldn’t pay their bills.

That’s why when we are dealing with memory management, it is very important to be careful with memory leaks.

Memory Leaks in iOS

Memory leaks are very related to how swift deals with memory management and it occurs when a determined object in memory could not be recovered by the Automatic Reference Counting (ARC). The ARC is responsible for tracking and managing your app’s memory usage and also freeing up the memory used by class instances when those instances are no longer needed.

A retaining cycle is one of the most common problems that can cause memory leaks in your apps. Every variable that you create has a strong reference by default. When you create a variable inside a class referencing another class, you have a strong relationship between the two classes.

The following example illustrates the idea:

Every time we create instances of those classes we are printing the objects that were initialized and also when they were deallocated from memory.

When we create a relationship between those objects and try to set those objects to nil, we don’t see the deinit print.

How to fix it?

We can break this strong retain cycle by marking one of the properties as weak or unowned.

The instances can then refer to each other without creating a strong reference cycle. As compared to strong, weak does not increment the reference count and ARC can release the objects from memory.

Monitoring memory leaks

We fixed the problem with objects having a bidirectional relationship, but there is another common way to create memory leaks and it is when we are using closures. Closure captures variables and constants from its surrounding scope. This creates a strong reference to it and the value needed by the closure.

It’s quite possible to have thousands of closures in our project and checking every one of them for memory problems could be very difficult. Xcode has a feature that allows us to monitor for memory leaks, all we need is to open Instruments e look for the option Leaks

After that, we select the simulator we are testing on and the application target.

For our example let’s take a look at the code below:

When we run the instruments we can see that we have a memory leak and the objects with the leak.

Checking our code we notice that our client and server have a strong relationship with each other.

We can fix the memory leak and change the property to weak or unowned:

Running again the instruments, we can see that there are no memory leaks and all deinitmessages are being printed in the console.

SecondViewController object was deallocated
Server object was deallocated
Client object was deallocated
Memory leak-free graph

Another tip is to go to the debug navigator and check the memory used. When there is a leak, the memory is not deallocated causing the memory usage to increase over time.

Continuous monitoring of memory leaks

Xcode Instruments is a great tool for finding potential memory leaks but sometimes it’s not easy to identify the problem. We also need to run it every time to check for a potential leak and monitor the memory usage.

We can use another technique to continuously monitor for potential memory leaks. Xcode allows us to create a symbolic breakpoint, in this case, we can create a breakpoint to listen to the view controller’s deallocmethod and check if the view controller was deallocated from memory.

Unit tests

We can also detect potential memory leaks by writing unit tests for our classes. This is by far the more scalable way to prevent any potential memory leaks.

After fixing the retaining cycle between the objects, we run the tests again:

Finally, we can write an extension on the XCTestCase and use it to test memory leaks on every object.

Final recommendations

  • A great rule of thumb is to always use weak or unowned when dealing with closures or delegates.
  • Create a strong code style for your project, this way the absence of a weak self will be noticeable.
  • Use some type of linter, like SwiftLint. This is a great tool that enforces you to adhere to a code style. You can detect issues at compile time and it helps automate code style for your team.

Wrapping Up

I hope you've enjoyed reading this article and discovered new ways to find memory leaks in your code.

Want to Connect?Linkedin | Instagram | Twitter

--

--