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

Can you manage memory and eat sushi at the same time?

Can you manage memory and eat sushi at the same time?

Update 4/12/13: These days, you should probably be using Apple’s new Automatic Reference Counting (ARC) technology instead of doing manual memory management. For more details on ARC, check out our ARC tutorial.

This is the first article in a three-part series on working with memory in Objective-C on the iPhone. (Jump to Part 2 or Part 3.)

When I review code from other developers, it seems that the most common mistakes are centered around memory management in Objective-C. If you are used to a language that handles memory management for you like Java or C#, things can be quite confusing!

So in this memory management tutorial, you’ll learn how memory management works in Objective-C by getting some hands-on experience. We’ll talk about how reference counting works, and go through each of the memory-management keywords by building a real-world example – an app about your favorite types of sushi!

This memory management tutorial is intended for beginner iOS developers, or intermediate developers in need of a refresher on this topic. Advanced developers might be more interested in some of the other tutorials on this site.

So without further ado, let’s get coding!

Getting Started

In XCode, go to File\New Project, choose iOS\Application\Navigation-based Application, and name the new project PropMemFun. Go to Build\Build and Run, and you should see an empty table view in your simulator like this:

Blank Screen from Navigation-Based XCode Template

Let’s say we wanted to fill this list in with our favorite types of sushi. The easiest way to do this is to make an array holding the string names of each type of sushi, and then each time we display a row, put the appropriate string from the array into the table view cell.

So start by declaring an instance variable for the sushi types array in RootViewController.h, as follows:

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {
    NSArray * _sushiTypes;
}

@end

By declaring this, each instance of RootViewController will have space to store a pointer to an NSArray, which is the Objective-C class you use for arrays that don’t change after initialization. If you need to change the array after initialization (for example, add an item into it later on), you should use NSMutableArray instead.

You may be wondering why we named the variable with an underscore in front. This is just something I personally like to do that I think makes things easier. I’ll talk more about why I like do this when I write a follow-up tutorial about Objective-C properties. But for now note that all we’ve done so far is add an instance variable – we haven’t done anything with properties yet – and we’ve named it with an underscore just as a personal preference, it doesn’t do anything special.

Now, go to RootViewController.m, uncomment viewDidLoad, and set it up like the following:

- (void)viewDidLoad {
    [super viewDidLoad];

    _sushiTypes = [[NSArray alloc] initWithObjects:@"California Roll", 
                   @"Tuna Roll", @"Salmon Roll", @"Unagi Roll", 
                   @"Philadelphia Roll", @"Rainbow Roll",
                   @"Vegetable Roll", @"Spider Roll", 
                   @"Shrimp Tempura Roll", @"Cucumber Roll",
                   @"Yellowtail Roll", @"Spicy Tuna Roll",
                   @"Avocado Roll", @"Scallop Roll",
                   nil];
}

Now we’re getting into memory management! Objects you create in Objective-C are reference-counted. This means that each object keeps track of how many other objects have a pointer to it. Once the reference count goes down to zero, the memory for the object can be safely released.

So your job, as a programmer, is to make sure that the reference count is always accurate. When you store a pointer to an object somewhere (like in an instance variable), you need to increment the reference count, and when you’re done with it you need to decrement the reference count.

“But OMG”, you may be thinking, “that sounds really complicated and confusing!” Don’t worry, it’s much easier to do than it sounds – let’s see with some hands-on!

Init the Sapporo, Release Your Inhibitions

Whenever you create an object in Objective-C, you first call alloc on the object to allocate space for it, then you call an init method to initialize the object. When the init method doesn’t take any parameters, sometimes you’ll see programmers take a shortcut by calling new instead (which is the same as alloc followed by init).

But the important thing is once this is all done, you’re returned a new object with the retain count set to 1. So when you’re done with it, you need to decrement the reference count.

Ok, so let’s give that a shot. Still in RootViewController.m, go to the end of your file, and set up your viewDidUnload and dealloc methods like the following:

- (void)viewDidUnload {
    [_sushiTypes release];
    _sushiTypes = nil;
}

- (void)dealloc {
    [_sushiTypes release];
    _sushiTypes = nil;
    [super dealloc];
}

Remember that when you created the array with alloc/init, it had a retain count of 1. So when you’re done with the array, you need to decrement the retain count. In Objective-C, you can do this by calling release on the object.

But where should you release it? Well, you should definitely release the array in dealloc, because obviously when this object is deallocated it will no longer need the array. Also, whenever you create an object in viewDidLoad (setting the reference count to 1), you should release the object in viewDidUnload. Don’t worry too much about this for now – some day I might write a separate memory management tutorial on that subject.

Note that you also set the object to nil afterwards. This is a good practice, because by setting it to nil it avoids a lot of problems. Any time you call a method on a nil object, it does nothing, but if you don’t set it to nil, if you tried calling a method on a deallocated object your program should crash.

Ok, now let’s make use of our new array. First replace the “return 0;” in tableView:numberOfRowsInSection with the following:

// Replace "return 0;" in tableView:numberOfRowsInSection with this
return _sushiTypes.count;

This says that the number of rows in the table should be equal to the number of rows in the sushiTypes array.

Now we need to tell the table view what to display for each row, so add the following tableView:cellForRowAtIndexPath, underneath where it says “Configure the cell”:

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

Let’s go through this line by line:

  1. Looks up the string in the sushiTypes array corresponding to the current row.
  2. We want to display a string such as “3: Unagi Roll”, where “3” is the row number and “Unagi Roll” is the name of the sushi for that row. An easy way to construct a string out of separate pieces like this is to use the initWithFormat initializer on NSString, so we call that here. Remember that after this is complete, the retain count of the returned string will be 1.
  3. Set the text for the current row to be the formatted string. The text label will make a copy of the string when it’s set.
  4. We’re done with the sushiString, so we release it. If you forget to call this, you’ll have a memory leak, because the string will have a retain count of 1 that is never decremented.

Compile and run your code, and if all works well, you should see the list of sushi in the table.

TableView listing Sushi