Objective-C: Memory Management

June 26, 2008

Note: What follows is another post in an on-going series for developers who are interested in learning to write applications for the iPhone. More information about writing for the iPhone can be found here: iPhone Developer Tips .

First off, it’s worthing clarifying that much of what is covered here is as much Cocoa as it is Objective-C. However, since the two are quite closely tied when it comes to developing Mac/iPhone applications, I’ve decided to keep with the Objective-C theme as it relates to the title of this and subsequent posts.

This post will review some of the nuances of memory management. I won’t go into all the details of memory management (see Objective-C Programming Guide , Cocoa Fundamentals and Memory Management Programming Guide for Cocoa ), rather, I’ll point out some of the more subtle concepts to keep in mind as you begin to work with objects.

Object Ownership

Rule #1 – If you create an object using alloc or copy, you need to free that object.
Rule #2 – If you didn’t create an object directly, don’t attempt to release the memory for the object.

For instance, here is an object that I “own” given that I’ve called ‘alloc’ to create the object. It’s up to me to release the memory for this object:

  TestClass *ptr = [[TestClass alloc] init];
 
  ...
 
  [ptr release];

Let’s look at two examples where we are not responsible for managing the memory associated with an object. First, when using a factory (aka convenience) methods of a class. For instance:

NSString *str;
str = [NSString stringWithFormat:@"Some string here..."];
NSLog(str);

The second example, is if you call an accessor method that returns an object. Assume that the object TestClass below has a getter method that returns a pointer to an instance variable named firstName that is an NSString.

  NSString *str;
  // No need to release this object
  str = [TestClass firstName];

This makes sense if you think about it. The getter simple returns a reference (pointer) to an existing object. Now, if you want to retain (express an interest in the object to keep it around) then you would need to use ‘retain’ to bump the reference count. However, if we simply want a pointer to the object as shown here, we don’t have responsibility to release the object.

Initializing Objects

The order of things happening when initializing an object is of great relevance. For example, look at this block of code:

  TestClass *ptr = [TestClass alloc];
  [ptr init]
 
  // Do something with the object
  if (ptr)
    ...

Seems harmless enough, right? Allocate an object and send a message to initialize the object. Here’s the problem, if the init method returns nil (which all good initializers do upon failure to properly initialize an object), the code on line 5 would pass the test (that is, ptr would not be nil).

Now compare that with this approach:

  TestClass *ptr = [[TestClass alloc] init];
 
  // Do something with the object
  if (ptr)
    ...

In the code above, assuming the init method returns nil upon failure, ptr will be set to nil, and the code on line 4 would not pass the test. Much better.

So, the lesson here is twofold: always return nil from an initializer method when the method fails to properly initialize the object. Second, it’s a good practice to combine a call to alloc and init into one step. We’ll talk more about initializers in a separate post.

Releasing Objects

Let’s talk about the other end, releasing objects. Have a look at the implementation for a simple class:

#import "TestClass.h"
 
// ================================
// = Implementation for TestClass =
// ================================
@implementation TestClass
 
-(NSString *) str
{
  return str;
}   
 
-(void) setStr:(NSString *)input
{
  [input retain];
  [str release];
  str = input;
}
 
-(void) dealloc
{
  [str release];
  [super dealloc];
}
 
@end

Look at the code below that creates an instance of the TestClass, calls the setter method to initialize the ‘str’ instance variable, and releases the memory for the ‘ptr’ object.

#import "TestClass.h"
 
int main(int argc, const char * argv[])
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
  TestClass *ptr = [[TestClass alloc] init];
  [ptr setStr:@"Fubar"];
 
  [ptr release];
  [pool drain];
  return 0;
}

The dealloc method in TestClass releases the memory for the ‘str’ instance variable, and it seems all is well. However, if at some point, somewhere inside the class implementation an attempt was made to check if the ‘str’ instance variable is nil (prior to doing some operation) for example:

if (str)
  do something...
else
  do something else...

the if statement would pass and the code on line 2 would be run. Not a good thing.

A better way is to replace the call to release with a call to the setter method and pass in nil . Look at the dealloc method below:

-(void) setStr:(NSString *)input
{
  [input retain];
  [str release];
  str = input;
}
 
...
 
-(void) dealloc
{
  [self setStr:nil];
  [super dealloc];
}

The reason this works is that you can send a message to a nil object without raising an exception. If you follow the trail you’ll see inside the setStr method that the variable ‘input’ (which is nil) is sent a message to retain . This is effectively ignored, as the object is nil . The next line releases the string ‘str’. The last line sets ‘str’ to the value of the input parameter (which is nil ). This prevents the problem in the previous example where ‘str’ still had a reference to a location in memory (even though the memory had been released).

As I was getting familiar with this, I wrote a short example in Xcode. Here is the test block for the dealloc method:

-(void) dealloc
{
  NSLog(@"Address of 'str' before release is: %ld", str);
 
  // After this, 'str' still points to a memory location
  [str release];
 
  // After this, 'str' will be nil  (** This is the preferred approach **)
  //[self setStr:nil];
 
  NSLog(@"Address of 'str' is after release is: %ld", str);
 
  // Notice the result of this based on how the instance var 'str' is released (above)
  if (str != nil)
    printf("'str' still points to a memory location (after being released)!");
  else
    printf("'str' now points to nil.");        
 
	[super dealloc];
}

Notice in this case that line 6 uses release. See the output below:

Now, if you comment out line 6 and remove the comment from line 9, you’ll get the following output. Notice how the reference to ‘str’ after is 0 (nil).

You can download the Xcode project here if you’d like to take a closer look at the complete example. Hopefully this information will save you some time trying to track down an elusive memory leak.

4 comments

Well done! Very comprehensive articles!

Thank you!

by Christos on Jun 26, 2008 at 5:31 am. #

Thanks much for these. Keep it up! :)

by george on Jun 26, 2008 at 2:55 pm. #

Your article came right in time. I was just stuck on how to retain and release object before copying.

Thanks
Eknath

by Eknath on Jun 30, 2008 at 5:52 pm. #

Comments are closed on this post. You can comment on this same post which has been moved to the iPhone Developer Tips blog: http://iphonedevelopertips.com/objective-c/memory-management.html

by john on Aug 16, 2008 at 7:33 pm. #