macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Knowing When to Let Go: Better Living Through Memory Management

by Rob Rix
06/10/2003

To get a reasonable picture of why memory management is such an important topic, you should first have a reasonable picture of what memory management actually is. In light of that, be prepared, because we're going to cover a fair amount of ground.

By the time we're finished you should have a good understanding of memory management not only as it pertains to Cocoa but also in general, which should be helpful in a variety of circumstances as memory management is fairly ubiquitous.

If You Love Something, Let it Go

The basic idea of memory management is, simplifying only a little, to keep objects (whether they be Objective-C objects or the more general "memory objects" or chunks of memory) around exactly as long as they're needed. Think about writing a program and how long each object will need to exist. In a perhaps typical document-based program, it might break down like this:

  • Program-lifetime objects--global instances of classes like NSApplication and NSWorkspace, as well as any data that you set up at launch time that's going to be around until the program quits. Program-lifetime refers to the fact that the objects in question have (approximately) the same lifetime as the program. These are pretty simple objects to keep around since you never have to delete them; the operating system will free up the memory when your application quits.

  • Document-lifetime objects--instances of classes like your NSDocument subclass, as well as the data your document class operates upon can be called document-lifetime objects because they'll only need to exist as long as the document is open.

  • Temporary objects--objects that are around for what is potentially a very short amount of time. These might be variables you declare inside a method, and release at the end, or inside a loop. In effect, these objects have the same lifetime as other variables declared in those methods, whether they be ints or floats or BOOLs or something else again.

These categories don't necessarily fit all kinds programs one might write, but they give a pretty good idea of how lifetimes break down. Recall that memory management means to keep objects around exactly as long as they're needed. So now let's look at a few strategies for doing that or as close to that as we can get.

Malloc/Free

Perhaps the most basic form of memory management that's still reasonably useful is malloc/free. These are two functions in the standard C library which allocate a chunk of memory and delete it (freeing it for use elsewhere), respectively.

The problem with malloc/free is pretty simple: what do you do when an object needs to be used by two chunks of code that can be run at different times? There is a very real and very dangerous possibility that one chunk of code will free the object before the other is done using it, and that it could be written over with data by a totally unconnected piece of code.

Retain/Release

Clearly objects need some sort of indicator to let them know when everybody's done using them. One solution is the retain/release mechanism, something that will be of interest to anyone writing software with Objective-C/Cocoa since it's the mechanism Cocoa uses.

With retain/release, each object has a retain count, an unsigned integer which indicates how many chunks of code need it to exist. Use of this mechanism breaks down as follows:

  • Creation: methods that create an object, like +alloc, -copy, and -mutableCopy, always set the object's retain count to 1.

  • Retain: the -retain method increments the retain count by 1. So if you've just -alloc'ed a new object, and you retain it, its retain count will then be 2 (starts at 1, retain adds 1).

  • Release: the -release method decrements the retain count by 1. If after this operation the retain count is 0, then -release will call the object's -dealloc method.

  • Destruction: in a well-coded object, the -dealloc method will -release all the object's instance variables that it created/retained, and then call [super dealloc] to give its superclass a chance to get rid of the ivars it's responsible for. This is also a good opportunity to close any files the object has opened and otherwise make sure that the object is completely done with. Except in your objects' implementation of -dealloc, you should probably never call -dealloc directly.

Now that we've got them out in the abstract, let's look at some examples of how these methods are actually used.

-(void)exampleOfRetaining
{
	id object = [[SomeClass alloc] init];
	NSLog(@"the retain count is %d", [object retainCount]);
	
	[object retain];
	NSLog(@"now it's %d", [object retainCount]);
	
	[object release];
	NSLog(@"and now it's %d", [object retainCount]);
	
	[object release];
}

Related Reading

Cocoa in a Nutshell
A Desktop Quick Reference
By Michael Beam, James Duncan Davidson

If we compile and run this code, we'll see that the object's retain count is always greater than zero. The first NSLog will tell us it's 1, the second that it's 2, and the third that it's 1 again. Why?

The first operation that touches object's retain count is id object = [[SomeClass alloc] init]. Looking at our list of retain/release methods again, we see that -alloc sets the retain count to its initial state of 1. Since we don't change the retain count after that, the first NSLog shows a retain count of 1. So far, so good.

Next we see [object retain]. Checking against our list we see that -retain increases the retain count by 1. This matches nicely with the second log, showing the retain count to be 2; it's as simple as 1 + 1.

After that, we see [object release]. Once again looking at our list, we see that half the responsibility of -release is decreasing the retain count by 1... and as we all know, 2 - 1 = 1, which explains the last NSLog output.

But what about that last [object release] right at the end? This line shows off the other responsibility of -release: after it decreases the retain count by 1, it notices that it's now 0. And since an object with a retain count of 0 doesn't need to exist any longer, it calls -dealloc, which releases the object's instance variables and ultimately frees the memory the object was occupying. Our object no longer exists, which is just as it should be.

This is perfect for situations where we don't have to return an object, but what about methods that create objects? We've already seen a few guidelines: +alloc, -copy, and -mutableCopy must all create a new object with a retain count of 1. But what about all those lovely "factory"-type methods like NSString's +stringWithFormat:?


Pages: 1, 2

Next Pagearrow