Finding memory leaks with the Xcode Memory Graph Debugger and fixing leaks with unowned and weak

In this tutorial, I’ll show you how to track down memory leaks within Xcode via the Memory Graph Debugger, new since Xcode 8. This is a powerful visual tool and doesn’t require you to step out of Xcode and start the Leaks Instrument. Once we identify some memory leaks, I’ll show you how to plug those leaks by using the Swift language’s weak and unowned qualifiers, and talk about the differences between the two qualifiers.

I recently discussed iOS memory management and memory leaks that occur when using reference semantics and reference types (classes) in my tutorials on “Swift 4 memory management via ARC for reference types (classes)” and “Fixing memory leaks — strong reference cycles — in Swift 4.” After reading these articles, you should understand how easy it is to inadvertently encode and introduce a strong reference cycle into your Swift 4 code and thus end up with a memory leak. You should also understand how generally straightforward it is to fix such a memory leak. My sample code in both tutorials was didactic. What about real-world projects with hundreds of thousands or millions of lines of code? Suppose that you’ve heard reports of diminished app performance, low memory warnings, or just plain app crashes. Finding memory leaks in your code is quite cumbersome when trying to debug via rote inspection, setting breakpoints, adding logging statements, etc.


In the real world, many developers are most likely to find out about memory leaks only after writing a significant amount of code — or only after their app is in production, especially in software shops that ignore testing. In this tutorial, I’ll show you how to catch memory leaks in large and/or complex code bases.

Xcode has gotten pretty smart at finding memory leaks. Let me show you how so you can decide for yourself about Xcode’s smarts.

CODE WITH MEMORY LEAKS

I can’t show you how to detect memory leaks if there are none to detect. You’ll recognize two classes in the code shown in this section, Student and Teacher, that exhibit a strong reference cycle as we discussed in a previous tutorial, and a new class, Course, which exhibits a strong reference cycle for a closure, that we will discuss shortly. Here are my class definitions:

Please review the code and try to find the memory leaks. Please note that I wrote class Course from scratch on purpose, specifically not reusing either Student or Teacher because I didn’t want to introduce any interdependencies between Course and the other two classes. Course has its own internal interdependency which leads to a memory leak.

Here’s some code that makes use of the Student, Teacher, and Course classes. Notice that because of reference type (class) design problems, all class instances I create get allocated, but never get deallocated — in other words, memory leaks occur. Here’s the code for allocation but no deallocation…

… and you can see the leaks in the Debug Area console (no deallocations):

In the next section, I’ll show you how Xcode itself can spot leaks, and help you find them even in very large code bases.

#ad

FINDING MEMORY LEAKS WITH XCODE ITSELF

Note that the name of the Xcode project I’m using in this tutorial is “Closure Retain Cycles.” You’ll see that name in several of this tutorial’s supporting graphic images.

If you have an app’s code base that you suspect is exhibiting the signs of memory mismanagement, open the project in Xcode and:

Set the active scheme to Debug:

Run the project:

• If you have an idea where your memory problems reside with the app’s flow, run through app features until you get there. If you don’t know where in the app’s flow to look, try exercising as many app features as possible. Once the project has been running for “enough” time, where you may have to experiment with the definition of “enough,” go to the Debug Toolbar in the Debug Area and click the Debug Memory Graph button:

• Once you click the Debug Memory Graph button, the Xcode screen will reconfigure as shown below:

All the purple squares with exclamation points indicate memory leaks.

• Take a look at the Debug Navigator pane on the left side of Xcode. Make sure the Show only leaked blocks button at the bottom of that pane is selected:

Let me zoom in to show you some features in this pane. First, notice that if I have the Show only leaked blocks button pressed, I can type keywords into a text field and find class names that are involved in leaks, like Student. Note that typing class names — filtering by class names — often causes the list of leaks in the Debug Navigator pane to fully populate with all your app’s leaks:

… but not always.

It’s switching to the Issue Navigator that shows all the memory leaks in your app, as shown here:

Now it’s time to see visual representations of your memory leaks as caused by strong reference cycles. Minimize the Debug Area and close the Assistant Editor (if applicable) so you can see the top, middle pane in Xcode. Choose the memory leak you want to examine in the Issue Navigator and then check out its corresponding diagram, like so:

EXAMINING AND SOLVING MEMORY LEAKS

There are two types of memory leaks in the code I showed you above and as found by Xcode’s Memory Graph Debugger. Both are based on strong reference cycles.

Strong reference cycle between two classes

We identified and solved the same strong reference cycle between the same two classes, Teacher and Student, in a previous post. In this tutorial, if you used the Debug Memory Graph button and then selected the Teacher and Student leaks in the Issue Navigator and then checked out their corresponding diagrams as I’ve explained herein, you should see why the Teacher and the three Student instances cannot be deallocated. Here’s the Teacher diagram where arrows denote references…

and here’s one of the three Student diagrams (they all look the same):

Just as discussed in my previous post, the Student objects all hold a strong reference to the Teacher object and the Teacher object holds a strong reference to each of the Student objects. Yes, the Teacher object does hold strong references to Student objects through an Array, but the Array maintains strong references to its elements by default:

Swift just doesn’t know how to break the reference cycle. Remember how we solved this memory leak by using the weak keyword? I leave it to you to recapitulate.

While I don’t have the time to describe all the features of the Memory Graph Debugger, look at the context menu which pops up if I [control]-click on an object in one of the memory graphs. I can jump to source code or print debug info (like addresses) to the console:

Strong reference cycle for a closure

The Course object exhibits a strong reference cycle for a closure within one instance of the class itself. That’s because the Course class defines a closure named roster.

I’m going to leave the definition of a closure for you to review, and just remind you that:

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you.

The word “capture” is very important here. In terms of my initial definition of the Course class, Apple states:


A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance… or because the closure calls a method on the instance… In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.

This strong reference cycle occurs because closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure. In essence, it’s the same problem as above–two strong references are keeping each other alive. However, rather than two class instances, this time it’s a class instance and a closure that are keeping each other alive. …

The [Course] instance’s [roster] property holds a strong reference to its closure. However, because the closure refers to self within its body (as a way to reference [self.students]), the closure captures self, which means that it holds a strong reference back to the [Course] instance. A strong reference cycle is created between the two.

We can visualize this strong reference cycle as in the previous tutorial thusly:

In this tutorial, if you used the Debug Memory Graph button and then selected the Course leak in the Issue Navigator and then checked out its corresponding diagram as I’ve explained herein, you should see why the Course instance and roster instance cannot be deallocated. Here’s the Course diagram where arrows denote references…

Notice how the Xcode diagram portrays the Course object with a cyclic arrow. That’s no accident as Course has a strong reference cycle.

Yes, the Course object’s roster closure does hold a strong reference to students through an Array, but the Array maintains strong references to its elements by default:

As you might imagine, fixing a strong reference cycle for a closure has everything to do with — what’s that word again? — capture. I defined the roster closure in the context of defining the Course class. The closure implicitly captured an instance of its enclosing class, Course, which we write in code as self. Notice I used the phrase “implicitly captured.” When you write a closure, remember that they “can capture and store references to any constants and variables from the context in which they are defined.” You don’t have to formally “declare” any constants and/or variables that you reference inside the closure from its surrounding context. Or do you?

The key to breaking a strong reference cycle for a closure is to be found in “Defining a Capture List.” We can declare and qualify constants and/or variables that will be referenced in a closure from the surrounding context in which our closure is defined. Just as I fixed the strong reference cycle — memory leak — which resulted from an interdependency (cross or cyclic reference) between the Student and Teacher classes using the weak qualifier, I can fix the strong reference cycle — memory leak — which resulted from an interdependency (cross or cyclic reference) between the Course class and its roster closure using the unowned qualifier. Here’s the new version of the Course class — and note that I’ve highlighted line 6:

So what exactly is unowned? It’s kind of like weak in terms of its strategic effect on memory management via ARC, as we discussed previously, but with several tactical differences worth mentioning:

#ad

Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

An unowned reference is expected to always have a value. As a result, ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using nonoptional types.

Apple has some guidance as to when to use weak and when to use unowned:

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. This enables you to check for their existence within the closure’s body.

NOTE: An unowned reference cannot be nil.

CONCLUSION – HAPPY DAYS

We just fixed the memory leak which resulted from an interdependency (cyclic reference) between the Course class and its roster closure. Let’s conclude by fixing the memory leak due to the strong reference cycle between Student and Teacher, reviewing all the code, running the code, reviewing Xcode console output, and finally, confirming there are no memory leaks in the code using Xcode’s Memory Graph Debugger.

Here are my three class definitions, with memory leaks plugged, as highlighted on lines 6,7,8 and 58,59,60:

Let’s run my code that allocates and deallocates instances of my Course, Student, and Teacher classes:

This is the Xcode console output from the previous code snippet:

We know everything is copasetic from the console output but, now that my code is running, let’s check for memory leaks using Xcode’s Memory Graph Debugger:

Look, ma, no memory leaks!

#ad

Author: Andrew Jaffee

Avid and well-published author, software engineer, designer, and developer, now specializing in iOS mobile app development in Objective-C and Swift, but with a strong background in C#, C++, .NET, JavaScript, HTML, CSS, jQuery, SQL Server, MySQL, Oracle, Agile, Test Driven Development, Git, Continuous Integration, Responsive Web Design, blah, blah, blah ... Did I miss any fad-based catch phrases? My brain avatar was kindly provided by https://icons8.com under a Creative Commons Attribution-NoDerivs 3.0 Unported license.