How To Debug A Memory Leak In An iOS App That Is Caused By A Third Party SDK (Part 2 of 3)

Xcode’s Allocations and Generations show your app’s memory footprint

Colin Contreary
The Mobile Mindset
5 min readJan 23, 2020

--

Photo by Harrison Broadbent on Unsplash

Part 2 — The Evil SDK

This is part two of a three part series on debugging a memory leak in an iOS app that is caused by a third party SDK.

👉 Part 1

👉 Part 3

In Part 1, we created an app called RecipeTime that loads recipes from a website into a WebView. We tested its memory footprint using Xcode’s Allocations and Generations tools.

In Part 2, we add an SDK to our app that introduces a memory leak. We investigate this using Xcode’s Allocations and Generations to learn how to spot a memory leak and what information can be gleaned from these tools.

We’ve got a lot to cover, so let’s get to it!

Adding Analytics

In Part 1, we tested our app’s memory performance and were happy with the result. By having our app destroy and create fresh WebViews for each network request, we were purposely optimizing for memory.

Let’s jump into the future a bit. We release our sample app to the App Store, and it starts getting users. After a period of time, we want to know some analytics. We want to learn what parts of the app users spend the most time on and which parts they don’t use. We also want help with performance monitoring and error debugging. We decide to add an analytics SDK to our application.

For this example, we’ve created AnalyzeTime, a simple analytics SDK. We integrate it into our app and look forward to all the insights it will provide us. (If you’d like to see the code for it, we’ll include it at the end of Part 3 so as not to spoil anything.)

Performance Drop Off

Uh oh, after adding the third party SDK, our app seems to be running slower. Much slower.

We know that the only thing different about our app is the SDK. So we email the SDK vendor and ask for assistance. Turns out they’re a little busy. Or unhelpful. Or both. (This is a hypothetical scenario. We are extremely responsive at Embrace. Feel free to ask our customers 😃)

It’s up to us now. We’re going to figure out what’s happened to our app. We know where to start, so let’s go back to Xcode’s Instruments panel!

Allocations, Part 2

We load the app up and examine the memory timeline.

Yep, there’s definitely a problem. The memory footprint is growing over time. We’ve got a nasty memory leak on our hands.

But how can we go about solving this? Well, we’re a little lucky in that our app is very simple. All it does is create a WebView, open a recipe on a website, then destroy the WebView, rinse, and repeat.

So maybe there’s a problem having to do with WebViews. If we dig into the Allocations List and look for WebViews, everything looks whaaaaaaaaaaaaaaaa

Remember before that we only had one WebView because the WebViews were being destroyed and created fresh each time. So it seems like they are no longer getting deleted. Or multiple WebViews are being created instead of one each time. Or one of a thousand other crazy things that we need to examine.

So what do we know at this point? We know something’s going wrong with WebViews, but we’re not sure what it is yet.

Let’s look at the Generations tool to see if we can learn anything more.

Generations, Part 2

If we examine the Growth column, we’re seeing much higher amounts of memory now. Anywhere between 300–1000KB of Growth between Generations. Remember, our performant and memory-efficient app only had 5KB of Growth.

If we examine each Generation individually, we notice some odd behavior with WebViews. The “# Persistent” column represents the total number of repetitions of a particular allocation. In our case, it shows us the number of new WebViews in memory in each Generation.

So Generation A had 5 WebViews in it. Then Generation B had 3 WebViews that weren’t in Generation A. Then Generation C had 3 WebViews that weren’t in Generation B. And on and on it goes.

You might be saying to yourself, “Well, our app’s code worked. Then we added a third-party SDK. Then performance suffered. Let’s look at their code.”

But unfortunately, most of the time you will not have access to the actual code base as most analytics SDKs are precompiled. The integration with them is kind of like black magic. There’s no way you can examine what’s inside there.

Or is there?

In Part 3, we’ll introduce Hopper, a disassembler and static analysis tool. With it, we can recreate the source code of a program from a compiled binary executable. This will allow us to examine the SDK and look for what is causing the memory leak.

What Have We Learned So Far

In Part 2, we dove back into Xcode’s Allocations and Generations to learn how to spot a memory leak. The Allocations timeline shows an app’s total memory footprint over time, whereas Generations are memory snapshots that highlight the difference in an app’s memory state from a previous time.

Generations can present the memory difference as a single number (representing the increase in total memory), and it can also show the difference in the amount of allocations. In our case, we used the “# Persistent” column to learn how many more WebViews were present in later Generations than earlier ones, showing us that perhaps our memory leak has something to do with mishandled WebViews.

We’d also love to hear comments, so we can improve our posts next time!

Who We Are

We work at Embrace, a mobile monitoring and developer analytics platform. We take the guesswork out of debugging, shortening the time it takes to solve bugs from hours or days to just minutes. If you’d like to learn more, you can check out our website or visit our docs!

See you in Part 3!

--

--