Memory Leak Detection in Runtime on iOS

Steve Dao
NE Digital
Published in
4 min readAug 20, 2020

--

Instruments

Xcode supports an awesome Instruments tool to help us detect the Memory Leaks & fix them. One downside of this approach is we have to launch the Instruments tool to find the leaks then fix them on Xcode & verify again on Instruments, these procedures take much time to finish, therefore we mostly run the Instruments after developing, sometime we forget :P

Sometime, we also find some leaks from other scopes (such as third-parties, framework, …) that we don’t really care because other guys are responsible to fix them instead of us. We only care about the leaks come from our codes. It would be great if we can detect the Memory Leaks of our codes in the Runtime then we can fix & test immediately right on the Xcode!

The whole idea is creating a Singleton LeakDetector instance to track the memory allocation of our objects & notify us when they’re leaked.

Core method of LeakDetector

In iOS, objects (reference type) are managed by ARC (Automatic Reference Counting) mechanism. If an object’s retain count is 0 then it should be deallocated immediately. If there’re any delays, it means that object is leaked!

The implementation of core method should call an assert method to notify us which object is leaked. Why assert? Why not precondition or fatal errors? Take a look in the Apple Documentation:

Use this function for internal sanity checks that are active during testing but do not impact performance of shipping code. To check for invalid usage in Release builds, see precondition(_:_:file:line:)

For the safe reason, let our App be forcefully crashed by assert in Debug to let us be aware instead of users in Release!

Now, let’s talk about the way to consider an object is leaked (deallocation delay). Our core method will keep a weak reference to given object from the App, after the deallocation expected time is up by a scheduled timer. If the object is not nil then it should be considered as a leak.

Not-nil object will be considered as a leak

By default, most of collection type such as Array, Dictionary, Set, … in Swift will retain a strong reference to its members. We should avoid this by using NSMapTable. It’s a recommendation for you to read this article to understand & get more use cases of this topic.

weak membership collection by NSMapTable

The inTime parameter above can be 1 second by default. We should support a cancelable mechanism to let us stop tracking the object, that’s why the LeakDetectionHandle is needed. This is only a core method, we also should support more convenience methods such as enablement & status, …

Interface of LeakDetector

We should keep in mind the Memory Leak detection should/must be done in Debug only, do not ship this mechanism to Production to avoid any unexpected behaviours. Therefore, the implementation of LeakDetector can be like this:

Toggle the LeakDetector by configurations

Now, our LeakDetector is ready to use. Let’s try to apply to some places:

use cases for applying LeakDetector

When a memory leak occurs, Xcode let app crash with a message to tell us which object is a root cause!

Assertion failed tells us which object has been leaked!

That’s all, I’ve just shared you guys the idea about the interface (protocol) of LeakDetectorType. There’re many ways to implement base on it.

I already implemented an open-source here: https://github.com/duyquang91/leakdetector

This idea & the implementations are inspired of Uber RIBs

Thanks for reading!

--

--