Channels ▼


Debugging Memory in iOS Apps

Listing Six presents another example of a memory leak. Here, the action method demoMemoryLeak: creates an instance of FooLink and stores it into the property posxLeak (lines 16-18). At first glance, the method looks correct. But if you kept invoking demoMemoryLeak: without disposing of the previous FooLink instance, you will end up with a leak.

Listing Six

// -- FooProblem.h
@interface FooProblem : NSObject
	// -- properties
	FooLink *posxLeak;
- (IBAction) demoMemoryLeak:(id)aSrc;

// -- FooProblem.m
@implementation FooProblem
- (IBAction) demoMemoryLeak:(id)aSrc
	// initialise the following private property
	posxLeak = malloc(sizeof(FooLink));
	posxLeak->fFoo = "foobar";
	posxLeak->fBar = rand();

The final common problem is the memory hog. This is where an object or structure uses up at least a quarter of the available memory. It holds on to its allocated memory until it is properly disposed of. An example of a memory hog is shown in Listing Seven. The action method demoMemoryHog: uses the instance method initWithContentsOfFile: to create an NSMutableArray object with data from the file (lines 6-11). If the source file is small, perhaps less than a half a megabyte, the array object will also be managably small. But what if the file is more than a megabyte in size? What if the read data results in a multi-dimensional array? Then you get an array object that can take up half or more of the available memory.

Listing Seven

- (IBAction) demoMemoryHog:(id)aSrc
	NSString *tPth;
	// initialise the following property
	tPth = [NSString 
	tPth = [tPth stringByExpandingTildeInPath];
	objcArray = [[NSMutableArray alloc]

Listing Eight is another example of a memory hog. This variant of demoMemoryHog: starts by creating a FooLink instance and populates its two fields (lines 7-9). Then, it proceeds to create a linked-list of 1024 nodes (lines 12-24). Once done, the resulting data structure stays resident, taking up precious memory unless all of the nodes are disposed of properly.

Listing Eight

- (IBAction) demoMemoryHog:(id)aSrc
	FooLink *tNode, *tTest, *tLink;
	NSInteger tIdx, tMax;

	// create the first link note
	tNode = malloc(sizeof(FooLink));
	tNode->fFoo = "foobar";
	tNode->fBar = 1;
	// create the rest of the link
	tTest = tNode;
	tMax = 1024;
	for (tIdx = 0; tIdx < tMax; tIdx++)
		// prepare the link node
		tLink = malloc(sizeof(FooLink));
		tLink->fFoo = "barfoo";
		tLink->fBar = tIdx + 2;
		// update the linked list
		tNode->fNext = tLink;
		tNode = tLink;
	// other things

There is one more memory problem that bears mentioning — heap corruption. Heap corruption happens when code in the app's heap region is altered unexpectedly. The effects are usually subtle, ranging from incorrect behavior to random crashes. There are many causes of heap corruption. It may be sourced to stack collisions or buffer overruns. Or it may be caused by any of the memory problems discussed earlier. As such, a definitive example of heap corruption is hard to present, and is omitted from the rest of this discussion.

Debugging by Static Analysis

Static analysis can identify potential memory problems in a project. You can then fix these problems while avoiding the overhead of runtime debugging. With Xcode, static analysis is done with the open-source tool Clang, which can handle both ANSI-C and ObjC source files. It works with the new LLVM compiler, available on version 3.2 or later of the Xcode IDE.

To start the analysis, choose Build and Analyze from Xcode's Project menu. Clang then highlights the suspect statement, and describes the problem. Figure 1 shows a sample result of one analysis. Function danglingObjC creates an NSString instance (tOrig) with the method initWithString:. It copies the object and assigns the copy to the local tCopy. Then it sends tOrig a release message and returns tCopy as its result.

Figure 1.

Here, Clang highlighted the last statement in the function. It warned that the tCopy result is a potential memory leak. This is because the copy message creates a separate instance of NSString. The release message affects only the tOrig object, not tCopy. One way to correct this error is to mark tCopy for autorelease as it is returned:

  return ([tCopy autorelease]);

Another way is to have the caller method dispose of the returned string object:

  tText = [self danglingObjC];
  // something with the string object
  [tText release];

Figure 2 shows another sample result. The action method demoDanglingPointer: starts by assigning an empty NSString instance to property objcString. Then, in a separate block, it creates another NSString instance (tBar), one with a value of "foobar". It sends a release message to tBar, but also uses tBar to create and assign a new NSString object to objcString.

Figure 2.

Here, too, Clang highlighted the last statement in demoDanglingPointer:. It warned that the NSString object assigned to objcString is potentially invalid. The memory region with its value "foobar" may be disposed of at any time, essentially making it a dangling pointer. One way to correct this problem is to move the code for creating tBar outside the code block. Another way is to create tBar with the factory method stringWithString:, and do away with the explicit release message:

  tBar = [NSMutableString stringWithString:@"foobar"];

This marks the string object for autorelease. As long as objcString stays valid, its reference to the object remains strong, preventing the latter's accidental disposal.

The Instruments Tool

Not all memory problems are easily caught through static analysis. Some appear only at runtime, and sometimes only after running a method or function several times. To detect these problems, you need to use the Instruments tool.

This tool uses the open-source framework DTrace to attach itself to an iOS app process and to acquire trace data. It graphs the trace data and lists the objects that make up the data. And it shows the stack frame that lead to each object.

Figure 3 shows a typical trace session. The session window divides itself into four panes; each pane can be hidden or shown. The Instruments pane consists of modules that gather and process the trace data. The Track pane displays the trace data from each module. The Details pane lists the objects and structures detected by the session. It can filter the list to a specific few, and it can drill-down on a specific object. The Extended Details pane reveals the stack frame for a given object. It also identifies the source file or library that made the object.

Figure 3.

Multiple sessions are supported. As a rule, though, only one session should be active to avoid confusion and to reduce impact on app performance. Trace data can be studied during or after acquisition, or it can be saved to the disk for later analysis.

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags


<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags


<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

