Memory Management

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, new or a variant of copy (e.g. mutableCopy), you need to free (release or autorelease) that object. This also applies if you send a retain message to an 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:

1
2
3
4
5
  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:

1
2
3
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.

1
2
3
  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:

1
2
3
4
5
6
  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:

1
2
3
4
5
  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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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.

1
2
3
4
5
6
7
8
9
10
11
12
13
#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:

1
2
3
4
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.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-(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.

36 Comments

  1. Well done! Very comprehensive articles!

    Thank you!

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

    Thanks
    Eknath

  3. Excellent article on Memory management. Thanks a lot for sharing .

    Regards.
    Srini Marreddy

  4. Thank you for the article. Very helpful. It answered most of my questions on Memory Management in Objective-C. I’m coming from .NET and things seem very different so far.

    Regards,
    Frank

  5. Memory management is probably the single most important topic in software development. It translates to efficiency, stability and security of any application. Your article has helped a lot in deciphering this concept for me. Thank you!

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

    Why is this not a good thing? The if statement is doing exactly what it’s supposed to do, isn’t it?

    Not trying to be obtuse here, but hitting my head against the wall trying to understand *why* this is not a good thing. I can’t see what’s wrong with this test.

  7. Kenneth,

    The reason this would not be a good thing is that in the code example shown, the dealloc will release the memory for str, however, releasing the memory for the str variable does not set the variable to nil, thus, if you later check to see if str is not nil (which happens in the if statement), the test will pass, not a good thing.

    Put another way, str has been released, but it still points to a memory location (even though that memory no longer belongs to str).

    Hope that helps.

    John

  8. I am new to Objective C and your articles helped a lot in my learning.

    I was expecting “[self setStr:nil];” to introduce memory leaks, as with the case of using only “str = nil;”. But a test using Instruments showed that I am wrong.

    It took me quite a while to realise “[self setStr:nil];” and “str = nil;”are NOT equivalent statements because the former is a method call and the following are executed:
    {
    [nil retain];
    [str release];
    str = nil;
    }

    Hope my sharing will help newbies to understand this great article better! Thanks.

  9. We can still uses self.str = nil; right? instead [self setStr:nil];

  10. Hi again John,

    Sorry for the delayed response!

    What I don’t understand though is… isn’t dealloc only run on an object when it is being removed from memory (ie, at the end of its life)? If the object in this example has itself been removed from memory, how would it be at all possible to access its str property?

    Kenneth

  11. very well explained…..

    superb…..

    Keep it up!!!!

  12. Hi,
    Congratulations for the article. It’s very good! I didn’t understand: “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:”

    The dealloc method won’t be execute only in the end? How at some point inside the class an attempt was made to check th ‘str’ AFTER dealloc? Is it possible?? I’m new in MAC programming..

    • Hi Waleska,

      I was referring to a developer inadvertently referring to ‘str’ after the variable has been released. If the variable is not set to nil, somewhere else in the code you could check the variable to see if it is nil as a means to know if it’s been deallocated, which could result in an attempt to access a variable that has been released.

  13. As a newbie into this world, I can say that I do not understand the section in this thread that covers the release of ‘str’ and the threat that ‘str’ still points to a memory location. Maybe someone can point out where my misunderstanding is?

    The sequence of events that is going on here is:

    int main(int argc, const char * argv[])
    {
    .
    .
    .
    TestClass *ptr = [[TestClass alloc] init];
    .
    .
    .

    }

    First event above is that an instance of TestClass is created; ‘ptr’ now references that object.

    int main(int argc, const char * argv[])
    {
    .
    .
    .
    TestClass *ptr = [[TestClass alloc] init];
    [ptr setStr:@”Fubar”];
    .
    .
    .
    }

    Next, the instance variable of ‘ptr’ (called ‘str’ from above) is now set to reference an NSString object with the value “Fubar”.

    int main(int argc, const char * argv[])
    {
    .
    .
    .
    TestClass *ptr = [[TestClass alloc] init];
    [ptr setStr:@”Fubar”];

    [ptr release];
    .
    .
    .
    }

    Next, the destruction of the instance of TestClass as referenced by ‘ptr’ begins. In the process of destroying ‘ptr’, the dealloc method of ‘ptr’ is called which, from above is:

    -(void) dealloc
    {
    [str release];
    [super dealloc];
    }

    The dealloc method releases the instance variable ‘str’ then calls [super dealloc] and the object no longer exists. At this point, it is not possible for any part of the class implementation (for this instance) to execute the code:

    if (str)
    do something…
    else
    do something else…

    because the object no longer exists. In face the only point that I see there is a threat is is you inserted some code between:

    [str release]

    and

    [super dealloc]

    Yes, after [str release] is called but before [super dealloc] is called the condition where ‘str’ points to a memory location which has already been deallocated is true. But since no one would do this the threat is zero. Am I not correct? What am I missing? Note that this is what the test code is doing: inspecting the value of ‘str’ after deallocating the memory to which it refers but before [super dealloc] is called.

    Looking at this a little deeper, if indeed the TestClass dealloc method was replaced with:

    -(void) dealloc
    {
    [self setStr:nil];
    [super dealloc];
    }

    You now have a memory leak, yes? The actual memory space allocated to hold the NSString ‘str’ has not been released. All that has happened is that the reference to the NSString object has been wiped clean but NSString ‘str’ still exists in all its glory.

    One further question, within the context of main(), the ‘str’ instance variable of TestClass has been set to “Foobar” as follows:

    int main(int argc, const char * argv[])
    {
    .
    .
    .
    TestClass *ptr = [[TestClass alloc] init];
    [ptr setStr:@”Fubar”];
    .
    .
    .
    }

    I assume, and please correct me if wrong, that this code snippet does NOT cause and instance of NSString with a reference count of 1 to be created within the context of main(), prior to the code inside setStr:(NSString *)s being executed. If this is not true then there is another memory leak since setStr:(NSString *)s retains the object which would bump its reference count to 2 and there is only one counteracting release in the lifecycle – the one in the dealloc method

    • Hi Andrew,

      When an instance variable is set to nil using this approach [self setStr:nil], the setter retains nil (effectively doing nothing), and as expected, releases the old value. Although somewhat confusing in syntax, this is a reasonable thing to do as the variable will now point to nil, not some random location in memory and the memory is freed.

      If you just called str = nil, you would have a memory leak as the variable is being set to nil, however it is not being released.

      Hope that helps.

  14. Actually, there is another problem with this code that i overlooked and that is the autorelease pool as follows:

    int main(int argc, const char * argv[])
    {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    .
    .
    .
    [pool drain];
    return 0;
    }

    Within the context of main() there are no objects that are being sent autorelease messages so creating a pool and destroying it is benign – it adds no value, it does no harm. For an introductory tutorial this could cause more confusion with newbies like myself – best to leave it out.

    Also, use of:

    [pool drain]

    implies that this application exists within a garbage collected environment and hence all chatter about memory management is irrelevant. If the application is not running in a GC environment the the correct code is:

    [pool release]

    • Andrew,

      The code example shown is how most all iPhone projects are setup inside the main method, where a pool is allocated and freed.

      If you look at the documentation for NSAutoreleasePool, drain states this:

      In a reference-counted environment, releases and pops the receiver; in a garbage-collected environment, triggers garbage collection if the memory allocated since the last collection is greater than the current threshold.

      In a reference-counted environment, this method behaves the same as release.

      If you look at the documentation for release, it states this:

      Special Considerations
      You should typically use drain instead of release.

  15. First of all – this is a really great article.
    However I do not understand one line in code:

    -(void) setStr:(NSString *)input
    {
    [input retain]; // Why ????
    [str release];
    str = input;
    }

    What is the purpose of: [input retain]; line for? And there is no release after all.
    I thing I do not understand some basic thing here.

    I would greately appreciate help with understanding it.

    • Hi Lukasz,

      Like any object that you want to “stick around” you need to call retain to bump the reference count. The code bumps the reference count for the incoming object, releases the old object and then assigns the instance variable to the new object.

      John

  16. John,

    Great article! However, I, like Andrew and Waleska, am having trouble understanding why it is necessary to set the pointer to nil if you are releasing the memory from within dealloc. As far as I can tell dealloc is only called when an object is being destroyed and therefore there should never be any code trying to access the str member of this instance. Is there some way for dealloc to be called and the instance remain?

    Josh

    • Hey Josh, maybe I need to reword that section :)

      This is really nothing more than a fail safe measure in case you inadvertently call a method on str. The result will not crash your, as it is legal to call methods on nil (which does nothing). It’s simply a way to make sure an object that is no longer in use is nil.

      John

  17. Hi, can you give example of using “retain” in accessor method? eg. in java it should be the following:

    public String getString(String str){
    return str;
    }

    I am not sure how to use retain in objective-c for this.

    Many thanks. :-)

    • Typically the accessor returns only a reference to an object, if you want to “keep the object around” the method that calls the accessor could follow this by sending a retain message to the value returned from the accessor, for example, [someObject retain].

  18. In your example regarding initializing:

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

    // Do something with the object
    if (ptr)

    if alloc returns a allocated TestClass object, and you then call init on it, and init can’t initialize and returns nil, what happened to the originally allocated object? Did init deallocate it before returning nil? No examples show this.

    • Paul, here’s my understanding – unless you specifically add code to an init method, the object would not be released. The general thought process is if alloc returns nil, then an init message will be sent to a nil object (legal in Objective-C), thus, ptr would be nil.

      If init could return nil, you would need to release any allocated memory inside the init method.

      I think I’ve got that correct, anyone else care to weigh in ?

  19. John,

    Thanks. I understand that if the alloc fails there are no issues. And, of course you would have to release any properties allocated. My question had to do with the object itself. Who is going to free that? Should there be a [self release] (or whatever the notation might be) in the init if, during the init something bad happens?

    Thanks

  20. Thanks for your helpful post!

    If I didn’t any retain operation to the str variable or a pointer which point to it, then I don’t need to call [self setStr:nil], right?

  21. thanks :)

    just a question:-

    what if in dealloc, if we do :-

    [str release], str=nil;

    is this correct ?

    • Yes, that is a nice clean way to release and set to nil – that is a good example of where the comma operator is handy.

    • If you declared retained property, you can make it even simpler:
      self.str = nil;
      It will release and set to nil in one step.

Comments are closed.