DirectNET

Data Center Management Solutions including UPS Systems, Data Center Cooling, KVM over IP & IP Power Strips, Server Racks and Server Rack accessories; KVM Switches and KVM Extenders; Rackmount Monitors and Rackmount Keyboards.


NAVIGATION
Home
Store
INSIDE MAC
Television Shows
Broadcast Shows
Daily News Shows
Special Shows
EVENTS
DAILY TIPS
Design
Mac OS X
Mac OS X UNIX
COMMUNITY
Forums
Surveys
NEWS
Current
Press
Archive
FEATURES
Editorial
Dr. Mac
Reviews
Reader Reports
RESOURCES
FAQ
Documentation
Learning Center
MAN pages
Glossary
Tutorials
Tips
Links

OUR PARTNERS

Tutorials 

Cocoa For Amateurs - Project 1 - Recognizing Problems with Memory Management

by Dr. John Timmer, Contributing Editor

Messing with Memory:

Now that we've got a rough idea of how memory management works, let's see what happens when we do things poorly - this way you'll know what the symptoms are if you do so yourself. We'll do this by using different methods of creating an instance of a basic Cocoa class, NSString, and then getting rid of it. We're going to enter an infinite loop, so that we can continue to allocate memory over time. In order to give us a little time to examine the program's activity before things get out of control, we'll also add a "sleep" statement to slow things down. Change the code in "main" to look like this:

int main (int argc, const char * argv[]) {
while (1 == 1) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSString *theString = [NSString stringWithString: @"blurk"];
[theString release];

[pool release];

sleep(2);
}
return 0;
}

Now, select "Build and Run", the button that looks like a hammer behind a terminal. You'll get the following result in the window that appears:
OutLauncher has exited due to signal 10 (SIGBUS).

Congratulations - You've just crashed your first program! To see where things went wrong, let's use the debugger. Set a breakpoint at the top of your code by clicking in the striped area to the left of the code editing area:

Now, hit the "Build and Debug" option, which looks like a hammer behind a spray can. This time, instead of running to completion, your program will be loaded into the debugger, and execution will halt at the breakpoint you set, with the debug window becoming active. To continue running the code line by line, hit the icon on the top right of this window that looks like an arrow above parenthesis. Once you advance far enough, two things should become apparent. The first is that the program crashed while executing the line where we released our Autorelease pool. The second is that, in the pane on the upper left of the debug window, you'll find the details about what was actually happening at the time of the crash - the key line to note is "NSPopAutoreleasePool". This is an important error to remember, since in most cases you won't be explicitly getting rid of your own Autorelease Pools. As a result, these errors will appear whenever the application disposes of a pool for you. In practice, the errors will be appear random, so you have to recognize them by the appearance of NSPopAutoreleasePool.

So, what's the cause of the error? The string wasn't created with an alloc, and it hadn't been given a "retain" message, so it was placed in the Autorelease pool. When sent a release, the memory holding the string was invalidated, even though the Autorelease pool still expects to deal with it. As a result, when the pool was released, it tried to clear theString, which no longer existed. If you're unsure of the retain status of an object, you can always check the status of an object by sending it a "retainCount" message - change the code to read:

NSString *theString = [NSString stringWithString: @"blurk"];
int tempInt;
tempInt = [theString retainCount];
[theString release];

Start debugging this; as you step through, watch the upper right pane of the debug window: you can track all of your variables as they change, and the most recently changed will be highlighted in red. Note that the retain count of theString is 1 - a retain count of zero mean that the object's no longer valid (and asking for the retain count of such an object would cause the program to crash again).

So, how to fix this? The easiest way is to get rid of the "release" message. If you did so, you'd find that the program could run indefinitely without error. An alternative would be to send the string a retain message when you create it:

NSString *theString = [[NSString stringWithString: @"blurk"] retain];

Or to explicitly allocate the string, which acts like a retain message:

NSString *theString = [[NSString alloc] initWithString: @"blurk"];

Let's see what happens when we try combining these. Make the code in main read:

NSString *theString = [[NSString alloc] initWithString: @"blurk"];
[theString retain];
[theString release];

Build and run this code, and you'll find that it doesn't crash. But there is a problem - go to the terminal and type:

ps -ax | grep "OutLauncher"

You should get back something that looks like:

1833 ?? Ss 0:00.04 /Users/drjay/OutLauncher/build/OutLauncher
1836 std UV+ 0:00.11 grep OutLauncher

The first number is our program's process ID - 1833 in this case. Use the process ID to check your program for leaks by typing "leaks 1833" (using your ID, not 1833). You'll find that your program is leaking badly, as leaks will return a list of leaked objects that will all look like:

Leak: 0x00055d50 size=30 instance of 'NSCFString'
0xa0130d40 0x000107f0 0x00055d40 0x00000005
0x00055590 0x00000000 0x00000000

Not surprisingly, it's leaking a string (that's all we used, after all) - noted as NSString's underlying representation, a Core Foundation string, CFString. What went wrong? Looking at the code, we both allocated the string and sent it a retain message, each of which retain the string. We only sent a single release message, though, which means that the string is retained even after we re-allocate it the next time through the loop. If we simply get rid of the retain message, this code will no longer leak.

Now that we've got some idea of what can go wrong with memory, you would think it would be time to start putting that knowledge to use. Before we do, however, we're going to take a bit of a detour tomorrow and look into RunLoops and executing code at regular time intervals.

If you have any questions or comments about this article, feel free to e-mail me at john_timmer@osxfaq.com

Copyright © 2000-2008 Inside Mac Media, Inc. All rights reserved.
Apple assumes no responsibility with regard to the selection, performance, or use of the products or services. All understandings, agreements, or warranties, if any, take place directly between the vendors and prospective users.
Apple, the Apple logo, Mac, PowerMac G4, PowerMac G5, Xserve, Xserve RAID, PowerBook, iBook, Airport, AirPort Extreme, iMac, eMac, iLife, iMovie, iCal, iPhoto, iTunes, QuickTime, FireWire, iPod, iSight, AppleWorks, Macintosh, Jaguar, Panther, Mac OS, Mac OS X and Mac OS X Server are trademarks of Apple Computer, Inc.