Memory Management Tutorial for iOS

A memory management tutorial focusing on memory management – the first in a 3-part series. By Ray Wenderlich.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Autorelease Your Potential

So far, you know that when you call alloc/init, the retain count is 1, and when you’re done with an object you need to call release to get the reference count to 0.

Next let’s talk about another important method you can call on an object – autorelease.

When you autorelease an object, it’s kind of like saying “I want you to be released at some point in the future, like the next time the run loop gets called. But for now I want to be able to use it.”

The easiest way to see it is in action. Modify tableView:cellForRowAtIndexPath underneath where it says “Configure the cell” to look like the following:

NSString * sushiName = [_sushiTypes objectAtIndex:indexPath.row]; // 1
NSString * sushiString = 
    [[[NSString alloc] initWithFormat:@"%d: %@", 
        indexPath.row, sushiName] autorelease]; // 2
cell.textLabel.text = sushiString; // 3

So, just two changes from last time. First you added a call to autorelease at the end of line 2. Second, you removed the release at the end.

Here’s how this works. After line 2, sushiString has a retain count of 1, but it has one autorelease pending. This means you can continue to use sushiString for the rest of this method, but the next time the run loop is called, the memory will go away.

In this case, that’s great for us because we want to use the sushiString right now, but don’t need it later on. However, if we tried to store the variable somewhere (without retaining it) and use it later on (such as when a button is tapped), we’d have a big problem, because we’d be trying to access released memory and the app might crash.

Sometimes when you call methods, they return objects to you that have a retain count of 1, but have an autorelease pending. You can see what I mean by modifying tableView:cellForRowAtIndexPath underneath where it says “Configure the cell” once more to look like the following:

NSString * sushiName = [_sushiTypes objectAtIndex:indexPath.row]; // 1
NSString * sushiString = 
    [NSString stringWithFormat:@"%d: %@", 
        indexPath.row, sushiName]; // 2
cell.textLabel.text = sushiString; // 3

The change here is just in line 2. Instead of calling alloc/init/autorelease, you now call a helper method on NSString called stringWithFormat. This method returns a new NSString with a retain count of 1, but has an autorelease pending. So just like the last example, it’s OK to use the string here, but you’d better not save it and try to use it again somewhere else (without retaining it) or the app might crash.

You might wonder how you can if a method returns an object with an autorelease pending or not. Well, there’s a simple convention that can tell you the answer to that:

  • If the method name begins with init or copy, the object returned will have a retain count of 1, and no autorelease pending. In other words, you own that object and have to release it when you’re done.
  • If the method name begins with anything else, the object returned will have a retain count of 1, and an autorelease pending. In other words, you can use the object right now, but if you want to use it later you have to retain the object.

Retain Your Wits

What if you have an object that has an autorelease pending that you want to use later on? You just need to call retain on it! That will bump the reference count up to 2, and later on when the autorelease fires it will go down to 1, and your object won’t be deallocated (since the reference count is still positive).

Let’s see how this works. Open up RootViewController.h and add another instance variable inside the @interface:

NSString * _lastSushiSelected;

This just makes a new instance variable, which will be used to keep track of the string corresponding to the last row selected.

Next modify tableView:didSelectRowAtIndexPath as follows:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    NSString * sushiName = [_sushiTypes objectAtIndex:indexPath.row]; // 1
    NSString * sushiString = [NSString stringWithFormat:@"%d: %@", 
        indexPath.row, sushiName]; // 2
    
    NSString * message = [NSString stringWithFormat:@"Last sushi: %@.  Cur sushi: %@", _lastSushiSelected, sushiString]; // 3
    UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:@"Sushi Power!" 
                                                         message:message 
                                                        delegate:nil 
                                               cancelButtonTitle:nil 
                                               otherButtonTitles:@"OK", nil] autorelease]; // 4
    [alertView show]; // 5
    
    [_lastSushiSelected release]; // 6
    _lastSushiSelected = [sushiString retain]; // 7
    
}

There’s a lot here, so let’s go over things line by line.

  1. Looks up the string in the sushiTypes array corresponding to the current row.
  2. Constructs a string representing the current row. Notice it uses the stringWithFormat method, which returns a new string. Since the method name does not begin with init or copy, you know that the retain count is 1, with a pending autorelease. Remember, that means you can use the returned string now, but if you want to use it later you have to retain the string.
  3. Constructs a message to display the last sushi selected and the current sushi selected. Just like the above, the stringWithFormat method returns an autorelease object, which works well for this case because you want to use it now, but don’t need it anymore after you display the popup.
  4. Creates an alert view to display the popup message. This is created with alloc/init, and then autorelease is called on the object so that it will be deallocated automatically later on.
  5. Shows the alert view.
  6. Before you can set the lastSushiSelected instance variable, you need to release the current lastSushiSelected object. If lastSushiSelected is nil, it’s OK, this will just do nothing.
  7. You want to be able to use the lastSushiSelected string later, so you need to retain it. Before you call retain, lastSushiSelected has a retain count of 1, and a pending autorelease. Afterwards, it has a retain count of 2, and a pending autorelease. When the autorelease takes effect, the retain count goes down to 1, and since the retain count is positive, it isn’t released yet, so it’s still safe to use the object later on.

There’s one more thing you have to add. To make sure there are no memory leaks, you need to release lastSushiSelected when dealloc is called on RootViewController. So add the following to your dealloc method:

[_lastSushiSelected release];
_lastSushiSelected = nil;

Basically, when your dealloc method is called, you need to release any objects you have referenced.

Compile and run your code, and now when you select a row, you should display the current row string and the last row string (which has been retained!)

Sushi Info in Alert View