As I am getting toward what I think is the end of coding for an application I hope to release soon, the nitty gritty work of fixing leaks, optimizing code, and squashing bugs has become the majority of what I’m doing now. Gone is the fun part of the application development process where I was creating features and solving new problems. It is now drudgery and focusing requires diligence. I know that the rewards are worth it as these final steps are what give an application stability and make it shine, but getting through it can be nothing but toil. Fortunately with the developer tools that shipped with Leopard, Apple has made this work much easier to handle in a little application called Instruments.
As usual, I’ve created a demo application that will allow you to see how things work. You can download it here: Leaky Demo Application. In this post I am simply going to show you how to use Instruments to detect memory leaks. It’s pretty straightforward, but there are few little nuances you may not be familiar with that once you understand will help you feel comfortable using this powerful tool.
Running Instruments From XCode
You can start Instruments directly and then navigate to your project build, however, it’s much quicker and simpler to start Instruments from XCdoe. To do so , open the demo project or a project of your own in XCode and select Run | Start With Performance Tool | Leaks.
While working on this project, Marcus and I discovered a few things that you should keep in mind while you are using Instruments.
- Breakpoints Do Not Break. Instruments utilizes debug information from your debug build, however, it does not stop at break points you set. This is because while you are loading your application into Instruments from the menu in XCode, Instruments simply uses the path of the current executable as its start path and loads it externally from XCode. The menu in XCode is really there as a convenience mechanism. This is not a big deal as you can always run again in Debug mode after your instruments session should you want your application to break. It’s just something to make a note of.
- NSLog Statements Do Not Show In The Debugger Console. If you want to see your NSLog statements, you will need to load the system Console application (/Applications/Utilities/Console).
Leaking Like A Sieve… Or Not
Our expectation was that when we allocated memory for a new NSString* object we would see it leak right away. This was, after all, our intention. Things, however, don’t always work the way you might expect. This code would not leak in our tests no matter what we tried.
NSString *string = [[NSString alloc] initWithString:@"Leaker"];
And here’s why. It turns out that since NSString(s) are immutable and since we are allocating the string using the @ operator, the compiler is making things more efficient for us. Strings with the same contents are given the same memory address. Here is a comment from the CocoaDev site that clarifies what I mean.
The nice thing about using the @”" notation for initializing NSString objects is that if you create two of them and their content is the same, the runtime uses the same storage for them. (Note, this is not guaranteed and certainly may not be the case across compilation unit or library boundaries. It is a performance optimization and the fact that it happens sometimes should not lead you to believe that you can safely, for example, use “==” to compare strings if you know they’re both from string constants.) (Ref. CocoaDev NSString)
In the example code, the NSString objects never leak and so they weren’t very useful. This isn’t to say that they never will leak, so you should be in the habit of calling release on your NSString(s), however, for our example project at least, the NSString leaks were never detected. We instead switched over to using NSMutableString at which point the application began to leak as desired.
When instruments loads your application, you will see the ObjectAlloc track start to fill in. To detect the leaks in your project, click on the Leaks track and then watch the Leak Status category which will detect leaks after the current timeout value. Alternatively, you can just click the Check For Leaks Now button under the Check Manually category.
Once the leak detection process has occurred you will see a list of objects that are currently leaking. To get details for any leaks in the list, click the object in the list and then click the Extended Detail button at the bottom of the Instruments screen.
Once you have clicked the Extended Detail button, you will see the stack trace list on the right side of the Instruments window. Look through the stack and locate any code highlighted with the color #CCFFFF which looks like this.
Note: This is actually inaccurate. The color may be different on your machine than on mine. Instead of looking for a color, look for your applications name and path and double-click that instead.
Thanks to Jason for pointing out the error.
Once you find the leak in your application, simply double click it and Instruments will return you to the exact line in XCode where the leak is happening.
To fix the leak, you need to make sure that you release the object. For more information and basic guidelines on memory allocation and deallocation, do a Google search. You can start with sites like this one: Very simple rules for memory management in Cocoa.
Finding and fixing issues is not the most glamorous aspect of application development, however, when done, the results are very rewarding. Instruments provides a streamlined way to track everything you would care about in your application. Watch this site for more tutorials on how to use other components available in Instruments. If you want to get an idea of what’s available, click on the Library button in Instruments and scroll through the ensuing list. It’s impressive.