macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Memory Management in Objective-C
Pages: 1, 2, 3

Cocoa's methods

Learning CocoaLearning Cocoa
By Apple Computer, Inc.
Table of Contents
Index
Sample Chapter
Full Description
Read Online -- Safari

The following discussion will be centered on four methods defined in the class NSObject. (NSObject is Cocoa's root class -- the granddaddy of the classes.) These methods are alloc, release, autorelease, and retain. There are several other methods in NSObject that you may find useful in your application, so take some time to browse through the class documentation. Below is a table describing the effect each of these four methods has on objects.

Method Description
+alloc

Creates a new object (allocating the necessary memory) and returns it with a reference count of "1".

-release

Decreases the receiver's reference count by "1".

-autorelease

Adds the receiver to the application's autorelease pool, which will decrease the receiver's reference count by "1" -- at some point in the future.

-retain

Increases the receiver's reference count by "1".

Our tools

To help us understand the way memory is managed in Objective-C, we will employ the services of two tools. One of them is an application that will let us execute code to demonstrate examples of good and bad memory management. You can download this application's project folder here.



The application is called MemApp. Its interface is a single button labeled "Go!" that is connected to an action method of the application's controller class called goButton. The action method contains code that we will manipulate to test different memory management situations. Additionally, I have created a subclass of NSObject called AClass, which is the class we will instantiate in goButton. AClass does not add any additional methods or instance variables to those defined by NSObject; its purpose is to have a unique name that will stand out among a sea of class names. The utility of this will soon be evident.

ObjectAlloc

The second tool we will use is an application provided by Apple as a part of the Developer Tools installation. It is called ObjectAlloc, and can be found in the /Developer/Applications/ directory.

This application controls the execution of your applications, and creates a table of every class used by your application (and there are a lot of these). Each class is associated with three numbers. They are the current number of instances of a class that exist in memory, the peak number of instances of the class that simultaneously exist in memory, and finally, the total number of objects of that class that have been created since the application began executing.

To get object allocation information for a particular application, you have to run that application from within ObjectAlloc. When you first launch ObjectAlloc, you will be presented with a "Run" dialog box, asking you to choose an application for ObjectAlloc to run. Our application can be found within the MemApp project folder at the path /MemApp/build/MemApp/Contents/MacOS/MemApp -- where the first MemApp is the name of the project folder, the second MemApp is the name of the application bundle (this will have a generic application icon associated with it in the file listing), and the last MemApp is the application within the bundle. The last MemApp is the one we want to run (it is also the only one that ObjectAlloc will allow us to run). Select it and click the Open button to bring up ObjectAlloc's main window.

For our purposes we will select the "auto-sort" check box at the bottom of the window. This will sort the class list alphabetically -- when AClass appears in the list of classes, it will appear at the very top directly beneath the totals row. The set of large buttons at the top of the window controls the application execution.

To start the application, click the Start Task button, which is the first button on the left. To stop the execution of the application, click the same button again. When ObjectAlloc starts running, you will see classes appear on the list as they are instantiated by the application. The class of interest for our experiment is, of course, AClass, but it won't appear on the list until the button is pressed to instantiate AClass for the first time. Below is an image of the setup of MemApp and ObjectAlloc running side-by-side.

Screen shot.

Our plan here is to run the application and observe how many instances of AClass exist before, during, and after the execution of the button's action method. For our purposes, we're going to assume that once the action method has completed running we no longer have a need for any instances of AClass. Let's see how it works out.

The good and the bad

Our first example is a situation where we won't even attempt memory management -- all we are going to do is instantiate AClass using alloc and leave it at that. The implementation file Controller.m contains the single action method containing the following code:

- (IBAction)goButton:(id)sender
{
    AClass *ourObject;
    ourObject = [AClass alloc];
}

As you can see, all we are doing is declaring a variable type to AClass, and then assigning it to the new object created with alloc. Load the executable into ObjectAlloc and start the task (make sure "auto-sort" is selected at the bottom of the window). MemApp will load and open up its main window. Click the "Go!" button, and watch as ObjectAlloc updates its list of objects present in memory. AClass should appear at the top of the list.

We see here that current, peak, and total counts all register one instance of AClass present in memory. If you keep pressing "Go!", you will see each of these counts increment for each call of the method.

However, after the code has finished executing, instances of AClass still remain in memory despite the fact that we're finished using it. This is not good, for several reasons. For one, we're wasting memory -- it's a memory leak. A small one, but still there. But we have bigger problems. After leaving the scope of the method (the scope of a name or a variable is the part of the program where we can use that name or variable) where ourObject is declared, there is absolutely no way we can communicate with the variable or the object that it points to. Our objects are lost somewhere in the memory space of out application -- marooned and cast away.

If ourObject had been declared as an instance variable with a global scope with respect to the class, the situation would be better because we could still access the object that ourObject points to -- even if it is outside of the method where it was created. The moral of the story is to not lose track of your objects. If you create an object using alloc, you own that object and it is your repsonsibility to properly dispose of it when you are done with it. Make sure you get rid of it before you forget about it.

The way we get rid of an object we've created with alloc is to send it a release or autorelease message. Either of these two methods will decrease an object's reference count by one, and because ourObject only has a count of "1" (from alloc), that will make its count "0", marking it for de-allocation. Let's see now how we can use these two methods.

The fix

The solution to this problem is to destroy each instance we create before we lose contact with it. In the code above, this would mean sending ourObject a release message before leaving the scope of ourObject (the method in this case). Later on we'll see the proper place to release objects assigned to instance variables. What we need to do now is send each allocated object a release message when we decide we're finished with it. Let's go into MemApp's Controller.m implementation file and change the method goButton as seen in the code below:

- (IBAction)goButton:(id)sender
{
    AClass *ourObject;
    ourObject = [AClass alloc];

    // Do stuff with ourObjectů.

    [ourObject release];
    ourObject = nil;
}

In the second-to-last line, we send ourObject a release message, bringing its reference count down to "0". The last line of code serves two imoprtant purposes. One, it helps us keep track of which variables still point to objects and which don't -- sort of a bookkeeping convenience. Its second purpose is more practical in that it helps us avoid problems associated with inadvertently sending messages to objects that no longer exist.

Sending a message to a nonexistent object will crash your application with a signal 10 (SIGBUS) or signal 11 (SIGSEGV) while your application won't even flinch at messages sent to nil. If you find yourself getting these kinds of crashes, check to make sure you're not trying to use an object that doesn't exist. (I've found in my experimentation that attempting to write to a nonexistent object will produce a Signal 11 segmentation violation, while attempting to retreive data from a nonexistent object will result in a Signal 10 bus error).

Open up the MemApp project and add these lines of code into Controller.m, then compile the application. From ObjectAlloc, stop the previous run of MemApp and restart it (the freshly compiled version will then be run). Now press the "Go!" button and look at the numbers for AClass. Here is what I got:

Parameter # Objects
Current 0
Peak 1
Total 1

What we see here is that no instances of AClass remain in memory at the end of our method. This is an example of effective memory management. Now we'll look at another way Cocoa allows us to release objects that produces a little more flexibility.

Pages: 1, 2, 3

Next Pagearrow