TweetFollow Us on Twitter

Developer to Developer: Thanks for the Memory-Part 2

Volume Number: 26
Issue Number: 11
Column Tag: Developer to Developer

Developer to Developer: Thanks for the Memory-Part 2

A look at how memory is managed in Objective-C

by Boisy G. Pitre

Introduction

If you followed last month's Developer to Developer column, you'll recall that we went through an introduction to memory management in Objective-C and Cocoa applications. That article was well suited to those who were new to Mac and iOS development, and touched on a number of concepts that are unique to Apple's development environment. For comparison, we also looked at how memory management is done in C and C++. Also, we touched on how Java programmers are greatly insulated from any memory considerations due to garbage collection.

This month we'll build upon the previous article and wade a little deeper into the waters of Objective-C memory management. We will start out by looking at a special subclass of NSObject that we can use to see just how our objects behave when they are created, retained, released and deallocated. We'll also take a look at the NSAutoreleasePool class, which gives us a convenient way to defer the release of an object and the return of its memory to the system. Finally, we will take Instruments, Apple's robust developer tool, for a spin.

Seeing Is believing

As we discussed last month, memory management in Objective-C consists of requesting an ownership interest in an object by retaining it, and relinquishing that ownership interest by releasing it. These operations can occur numerous times within an application for the same object. Balancing them is key to insuring that the memory that an object occupies is not left to leak while the application runs. Conversely, we must guard against the memory being returned to the system too soon, which could result in a crash, depending upon how that object is accessed later.

How convenient would it be to actually visualize this process happening in real-time? Well, it can be done, and quite readily, by subclassing NSObject and creating a new base class which overrides the retain and release methods, among others. And that's exactly what we will do. I highly recommend that you follow along by downloading the code for this month's article from the MacTech FTP source archive at ftp://ftp.mactech.com and launching the project file in Xcode. The application is made up of simple, contrived examples that are useful in illustrating the mechanics of object allocation and deallocation.

When determining what methods to override, we can consult two resources: the NSObject Class Reference (available in the Xcode's Developer Documentation, accessible from the Xcode Help menu) or the NSObject.h header file itself. Let's take the header file route and take a peek at NSObject.h directly. To do this easily from within Xcode, go to the File > Open Quicky... menu option, then type NSObject.h. It should appear in the drop down box; select it and it will be displayed in an Xcode text editor window.


Figure 1 - Opening the NSObject.h header file

The header file is composed of several sections: basic protocols, the base class, discardable content and object allocation/deallocation. For this article, we're going to focus on methods in the basic protocols and base class sections of the header file (starting at lines 11 and 63 respectively of the header file in the 10.6 SDK). Of the methods declared in the basic protocols section, let's override the following:

- (id)retain;
- (oneway void)release;
- (id)autorelease;

Similarly, let's override the following methods declared in the base class section:

- (id)init;
- (void)dealloc;

Lastly, for informational purposes, we'll override this method:

+ (id)allocWithZone:(NSZone *)zone;

There may be some questions as to the choice of methods to override. Remember, we are trying to capture the memory management operations in a way that allows us to visually confirm them as they happen. Most of the above methods are vectors for the retain count changing. By overriding these methods with methods in a subclass that (a) calls the same method in NSObject, and (b) logs the call itself and the retain count, we can see Objective-C memory management in action.

As mentioned earlier, overriding the methods requires subclassing NSObject. We'll create a new class just for the Developer to Developer column, DDObject, as a direct descendant of NSObject, so any class who inherits from DDObject will automatically benefit from the methods that we will embellish. Let's start out by looking at the code for the retain and release methods:

- (id)retain;
{
   id result = [super retain];
   NSLog(@"[%@ retain] (retainCount = %d)", [self className], [self retainCount]);
   return result;
}
- (oneway void)release;
{
   NSLog(@"[%@ release] (retainCount = %d)", [self className], [self retainCount] - 1) withLevel:TBLogLevelInfo];
   [super release];
}

Both methods mimic the return values of their original definitions in NSObject.h and use the NSLog() function to show the name of the class and the retain count. The retain method returns the retain count after the superclass' retain method is called, while the release method shows the retain count first. This ordering insures that we see the true retain count value at the appropriate place and time.

The init and dealloc methods are also points where observing the retain count can be instructive, so we'll extend these as well:

- (id)init;
{
   if (self = [super init])
   {
      NSLog(@"[%@ init] (retainCount = %d)", [self className], [self retainCount]);
   }
   
   return self;
}
- (void)dealloc;
{
   NSLog(@"[%@ dealloc]", [self className]);
   [super dealloc];
}

Even though we explicitly call retainCount in the init method, it will always print a retain count of 1. The dealloc method is called only when the retain count goes to 0; since a release call precedes this, we'll forego logging the retain count, and simply log that we are deallocating the object's memory here.

Finally, we'll override the allocWithZone: method, which will clue us in when an allocation is taking place (as we'll see shortly, the alloc method actually calls allocWithZone: with a zone of 0):

+ (id)allocWithZone:(NSZone *)zone;
{
   NSLog(@"[%@ allocWithZone:%d]", [self className], zone);
   return [super allocWithZone:zone];
}

Now that the pieces are in place, let's take a look at memexplore.m. This file contains the main() function, several test functions, and the interface and implementation for the MemObject class. This class extends the DDObject class which we just reviewed, so we would expect to see some interesting output when we run the test. Let's go ahead and do that now. First, build the memexplore target (Build > Build). Next, ensure that the Debugger Console is in focus so that the logging results can be seen (Run > Console). Finally, run the application (Run > Run). A menu appears where you can type the number of the test to perform. Let's run the allocRunRelease test by typing 1 then the return key.


Figure 2 – Output of the allocRunRelease test

The output of this test clearly shows the steps in which our MemObject comes to life, runs, then is finally released. As expected, the init method shows that the retain count is 1. The run method is then called, and finally the release method. Recall that this method decrements the retain count by 1; this is confirmed by the retain count falling to 0, and the dealloc method being implicitly called after that. Just to be convincing that the retain count can go higher, the allocRunReleaseMultiple test performs a retain after allocating and initializing the MemObject object, as well as an extra release. The net effect is the same: the object's dealloc method is called when the retain count falls to 0. Go ahead and run this test as well.


Figure 3 - Output of the allocRunReleaseMultiple test

One might conclude that the "hands-on" memory management aspect of Objective-C is a bit laborious. After all, we not only explicitly allocate and initialize an object, we must also take care to properly release it. Not releasing an object after we are done with it denies the use of that memory elsewhere; if an application doesn't release an object properly, it will stay around until the application quits, which could be a short time or a long time. Even though current systems contain gigabytes of memory and can accommodate a bit of sloppy memory management, it is considered bad programming practice to tolerate memory leaks. On mobile devices such as the iPhone and iPad where resources are limited, it is even more critical to wipe out these types of memory leaks.

Swimming In The NSAutoreleasePool

As we discussed, balancing retains with releases insures that we avoid memory leaks or crashes. It is a disciplined approach, and forces us to think about the lifetimes of our objects. However, Cocoa gives us a bit of a reprieve from the tedium of retain count management. We can make things a little easier for ourselves by conveniently deferring the return of an object's memory to the system using a special type of class provided by the Cocoa framework: NSAutoreleasePool.

An autorelease pool acts as a sort of dumping site for objects; upon receiving an object, the pool dutifully records a reference to the memory location of objects for later releasing. Any object can be relegated to the autorelease pool by having the autorelease message sent to it:

   SomeObject *s = [[[SomeObject alloc] init] autorelease];

The autorelease message allows us to pass complete responsibility of releasing the object to the NSAutoreleasePool. By doing this, we essentially wash our hands of further worry about the lifetime of the object and the memory that it is taking up. In the above code fragment, the object s receives the autorelease message after the alloc and init messages; subsequent to that, we can use the object as needed but should not send the release message to the object. That will be performed by the autorelease pool at a later time.

Exactly where autorelease pools are created depends upon the context in which you are writing your program. In Cocoa applications, an autorelease pool is created for you automatically, so you don't have to concern yourself with its creation. However, if you are using threads, you will need to create and manage your own autorelease pool for that thread. And in the case of a command line based program such as memexplore, it is necessary to create an autorelease pool as well.

Autorelease pools can be created many times, with the most recent pool being the one that receives any objects that receive the autorelease message. In essence, autorelease pools are stacked as they are created. When the topmost autorelease pool is destroyed, the next autorelease pool will receive autoreleased objects. Destroying an autorelease pool is similar to destroying any object: sending a release message to the pool will cause it to in turn send release messages to all objects that it holds, and finally the pool itself is deallocated. A slight twist on this is that since the release of Objective-C 2.0 and garbage collection, the drain message is the desired message to send to an autorelease pool instead. This message performs the same function as the release message, but does additional work in a garbage collected environment. For our code, we'll use the drain message when releasing our pools.

Can we peek into an autorelease pool? We certainly can, thanks to the NSDebug.h header file's NSAutoreleasePoolDebugging category. The showPool message, when sent to an autorelease pool object, will display the contents of all pools in the pool stack to the standard error path. We use this debugging method in several of our test programs to illustrate what the pool looks like just before it is drained. To illustrate this, run test 1 (allocRunRelease) then test 5 (allocRunReleaseWithPoolOverRetain). The final output will look like this:

==== top of stack ================
  0x100110b00 (NSCFString)
  0x100110ae0 (NSCFString)
  0x100110920 (MemObject)
  0x100110a70 (NSCFString)
  0x100110a50 (NSCFString)
  0x100110a30 (NSCFString)
==== top of pool, 6 objects ================
  0x1001109b0 (NSCFString)
  0x1001109e0 (NSCFString)
  0x100110990 (NSCFString)
  0x1001108b0 (NSCFString)
  0x100110070 (NSCFString)
==== top of pool, 5 objects ================

The pool appearing on the very top of the stack was created in the allocRunReleaseWithPoolOverRetain() function and has 6 objects, including a MemObject which we overretained and is just leaking. The next pool was created in the main() function has 5 objects. You may be wondering what is going on with all of the NSCFString objects in the autorelease pool. Those are the various string literals which appear in the NSLog() functions that have been executed during the program's run. As objects themselves, these strings require memory management, and they are automatically added to the autorelease pool when initialized.

Pool Hazards

As convenient as autorelease pools are, they must be used with care. The same problems that we discussed last month (overretaining/underreleasing or overreleasing/

underretaining) can still lead to memory leaks or crashes. A common mistake that many beginning programmers make is to send a release message to an object after it has been sent an autorelease message. The net effect of that transgression is that the object will end up being overreleased, and a crash is likely. The allocRunReleaseWithPoolOverRelease test illustrates this poignantly.

   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   NSLog(@"- allocRunReleaseWithPoolOverRelease -");
   MemObject *s1 = [[[MemObject alloc] init] autorelease];
   [s1 run];
   [s1 release];
   NSLog(@"----------------");
   [NSAutoreleasePool showPools];
   [pool drain];

Note that the autorelease message is sent to the s1 object upon creation, then a release message follows. It is at that point which the object's retain count goes to 0 and its dealloc method is called. Since its reference is also in the autorelease pool, the simple act of sending the showPools method is enough to trigger an access exception and crash the program.

Another interesting hazard is having no autorelease pool at all. Without an autorelease pool, objects sent an autorelease message (including NSCFString string literals that we saw above) have nowhere to go, and just leak all over the place. If you ever see a message like this coming from your application:

*** __NSAutoreleaseNoPool(): Object 0x100110770 of class NSCFString autoreleased with no pool in place - just leaking

then you know that somewhere, an autorelease pool is needed but doesn't exist. This most commonly happens when using threads and failing to create an autorelease pool. As a rule, your thread's entry point method should create an autorelease pool at the beginning, then drain the pool at the end.

Inspecting Memory Leaks With Instruments

Apple's Instruments is part of the Xcode development suite, and is a powerful tool for strengthening and bulletproofing your applications in a number of areas. When it comes to memory usage and management, it is particularly useful, and has handy two templates that are a must: Allocations and Leaks. The former template shows you exactly what objects and how many are being allocated by your application. The latter gives you insight into where your application may be leaking memory.

Typically, Instruments can be invoked from within Xcode's Run menu. Given that our application is a windowless application whose input and output appear on the console, we'll start Instruments from the Finder and then attach to the running process.

Before starting Instruments, go ahead and run the memexplore program from within Xcode. Now let's start Instruments by navigating to the /Developer/Applications folder in the Finder and double-click the Instruments icon. You will see a window with a drop-down sheet asking for the template to use. Select the Leaks template and click the Choose button.


Figure 4 - Selecting the Leaks template from Instruments

With the template chosen, you will see the main Instruments window with both the Allocations and Leaks templates shown. Since our application is running, we can attach to it from the Choose Target button and selecting the Attach to Process menu item, then navigate the list of running processes until we find memexplore. After memexplore has been selected, click the Record button in the toolbar. This starts the process of recording all object allocations as well as the leak detection procedure.


Figure 5 - Selecting the memexplore process from Instruments

With Instruments recording the memexplore application, switch to the Xcode console and select test 5 (allocRunReleasePoolWithPoolOverRetain). This test purposely performs an extra retain to the object so that it will leak. After the test is run, switch back to Instruments and click on the Leaks template header on the left side of the window. Within a a short time, the leaked object name should appear, along with the address and size. Clicking the third button of the view group in the toolbar will reveal the extended detail including the stack trace where the leak occurred. As we can see in the stack trace, the DDObject's allocWithZone: method is where the leak originated.


Figure 6 - Instruments showing the memory leak in memexplore

Summary

As we have seen, mismanaging an object's lifetime through its retain count can have ramifications for the health of your applications. Autorelease pools give us some convenience but even so, we must still be vigilant when balancing our retains and releases. Crashes are often caused by objects being released prematurely, and leaks are the result of retaining an object beyond its lifetime. It's times like these when Apple's Instruments can pinpoint the exact spot where the leak occurred, and we can take corrective action. For those of you who have started delving into Objective-C, these articles and the accompanying code should give you a basis for understanding memory management as well as a springboard to further experimentation.

Bibliography and References

Apple. Instruments User Guide. http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/InstrumentsUserGuide.pdf

CocoaDev. DebuggingAutorelease.

http://www.cocoadev.com/index.pl?DebuggingAutorelease


Boisy G. Pitre lives in Southwest Louisiana and is the lead developer at Tee-Boy where he also consults on Mac and iOS projects with a variety of clients. He holds a Master of Science in Computer Science from the University of Louisiana at Lafayette. Besides Mac programming, his hobbies and interests include retro-computing, ham radio, vending machine and arcade game restoration, and playing Cajun music. You can reach him at boisy@tee-boy.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Ableton Live 10.1.1 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
BetterTouchTool 3.202 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
Fission 2.4.6 - Streamlined audio editor...
Fission can crop and trim audio, paste in or join files, or just rapidly split one long file into many. It's streamlined for fast editing. Plus, it works without the quality loss caused by other... Read more
Drama 1.0.27 - Prototyping, animation...
Drama's handy 3-in-1 functionality uniquely integrates design, animation and prototyping into a single familiar tool. No more frustrating switching between apps or learning new stuff. And by... Read more
Adobe Lightroom Classic CC 8.4.1 - Impor...
Adobe Lightroom Classic is available as part of Adobe Creative Cloud for as little as $9.99/month bundled with Photoshop CC as part of the photography package. Adobe Lightroom Classic CC (was Adobe... Read more
iExplorer 4.3.2 - View and transfer file...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
Adobe After Effects CC 2018 16.1.3 - Cre...
After Effects CC 2018 is available as part of Adobe Creative Cloud for $52.99/month (or $20.99/month for a single app license). The new, more connected After Effects CC 2018 can make the impossible... Read more
Adobe Audition CC 2019 12.1.4 - Professi...
Audition CC 2019 is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition CC 2019 empowers you to create and... Read more
Adobe Premiere Pro CC 2019 13.1.5 - Digi...
Premiere Pro CC 2019 is available as part of Adobe Creative Cloud for as little as $52.99/month. The price on display is a price for annual by-monthly plan for Adobe Premiere Pro only Adobe Premiere... Read more
Navicat Premium Essentials 12.1.25 - Pro...
Navicat Premium Essentials is a compact version of Navicat which provides basic and necessary features you will need to perform simple administration on a database. It supports the latest features... Read more

Latest Forum Discussions

See All

Marvel Strike Force is adding Agent Coul...
Marvel Strike Force, the popular squad-based RPG, is set to receive a bunch of new content over the next few weeks. [Read more] | Read more »
Lots of premium games are going free (so...
You may have seen over the past couple weeks a that a bunch of premium games have suddenly become free. This isn’t a mistake, nor is it some last hurrah before Apple Arcade hits, and it’s important to know that these games aren’t actually becoming... | Read more »
Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »

Price Scanner via MacPrices.net

4-core and 6-core 2018 Mac minis available at...
Apple has Certified Refurbished 2018 Mac minis available on their online store for $120-$170 off the cost of new models. Each mini comes with a new outer case plus a standard Apple one-year warranty... Read more
$250 prepaid Visa card with any Apple iPhone,...
Xfinity Mobile will include a free $250 prepaid Visa card with the purchase of any new iPhone, new line activation, and transfer of phone number to Xfinity Mobile. Offer is valid through October 27,... Read more
Sprint is offering the 64GB Apple iPhone 11 P...
Sprint has the new 64GB iPhone 11 Pro available for $12.50 per month for new customers with an eligible trade-in in of iPhone 7 or newer. That’s down from their standard monthly lease of $41.67. The... Read more
Final week: Apple’s 2019 Back to School Promo...
Purchase a new Mac using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address qualify for the discount... Read more
Save $30 on Apple’s AirPods at these reseller...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP as part of their Labor Day sale. Shipping is free: – AirPods with Charging Case: $144.95 $15 off MSRP – AirPods with... Read more
Preorder your Apple Watch Series 5 today at A...
Amazon has Apple Watch Series 5 GPS models available for preorder and on sale today for $15 off Apple’s MSRP. Shipping is free and starts on September 20th: – 40mm Apple Watch Series 5 GPS: $384.99 $... Read more
21″ iMacs on sale for $100 off Apple’s MSRP,...
B&H Photo has new 21″ Apple iMacs on sale for $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Overnight shipping... Read more
2018 4 and 6-Core Mac minis on sale today for...
Apple resellers are offering new 2018 4-Core and 6-Core Mac minis for $100-$150 off MSRP for a limited time. B&H Photo has the new 2018 4-Core and 6-Core Mac minis on sale for up to $150 off... Read more
Save $150-$250 on 10.2″ WiFi + Cellular iPads...
Verizon is offering $150-$250 discounts on Apple’s new 10.2″ WiFi + Cellular iPad with service. Buy the iPad itself and save $150. Save $250 on the purchase of an iPad along with an iPhone. The fine... Read more
Apple continues to offer 13″ 2.3GHz Dual-Core...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases are new... Read more

Jobs Board

*Apple* Mobility Pro - Best Buy (United Stat...
**719499BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 001266-Charleston-Store **Job Description:** At Best Buy, our Read more
Best Buy *Apple* Computing Master - Best Bu...
**733266BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000144-Union City-Store **Job Description:** **What does a Read more
Best Buy *Apple* Computing Master - Best Bu...
**730765BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000565-St Petersburg-Store **Job Description:** **What does Read more
*Apple* Mobile Master - Best Buy (United Sta...
**725617BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 001095-Chesterfield-Store **Job Description:** **What does a Read more
Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.