Categories
Cocos2d-iPhone Programming

Objective-C Memory Management For Newbies

Below you will find a list of memory management rules that will make your Coco2d game coding experience easier and your games less buggy. But before you dive in please read the caveats below:

  • These rules are based on several sources written by engineers with much more experienced than me in Objective-C and Cocos2D development. Any mistakes are my own, any good idea belong to them.
  • My sources include this tutorial by Ray Wenderlich and the comments by fufie. Ray’s tutorials should be read by every aspiring Coco2d-iPhone developer!
  • I also found this Stack Overflow question and answer on property declarations and this blog post on properties by CocoaCast really helpful as well.
  • Of course all this info is contained in Apple’s Memory Management Programming Guide in greater detail. RTFM, as they cheerfully remind us in IRC, is a great way to learn.
  • In many ways this blog post is just a briefing on all of the above material and my motivation for posting this info is just to have it all in once place and neatly summarized.
  • Finally, these rules are guidelines for inexperienced developers, not laws. Engineers who are experienced with Objective-C memory management and Cocos2d don’t need these rules and shouldn’t even bother to waste their time and read my post. Following these rules will keep newbies out of trouble with their first game or two.

10 rules that make memory management a breeze…

Rule #1: Always create properties for your ivars in your .h and synthesize them in your .m.

// in MyClass.h
@interface MyClass : NSObject {
@public NSString* _moniker;
}
@property (nonatomic, copy, readwrite) NSString* moniker;
@end

// in MyClass.m
@implementation MyClass
@synthesize moniker = _moniker;
@end

Rule #2: Use an underscore so the compiler can distinguish an ivar from a property with a common root name. When synthesizing remember to associate the ivar with the property. This way the compiler will yell at you if you try to set an ivar as if it were a property.

@synthesize moniker = _moniker; // ivar = property

Rule #3: Always access your internal properties through self using dot syntax to ensure proper memory management through property declarations.

self.moniker = @"Betty"; // the ivar _moniker now refers to a new copy of the string "Betty"

Note: In C, C++, and Objective-C the operand on the left must be an lvalue so you can’t chain assignments. Code like

self.rectangle.top = 10;

generates a compiler error.
Instead you have to unpack the object and assign it back to the property:

Rectangle* rect = self.rectangle;
rect.top = 10;
self.rectangle = rect;

Rule #4: Always provide an object factory class method to create an instance of your custom objects using autorelease for simple memory management.

+(id) getInstance {
	return [[[self alloc] init] autorelease];
}

Rule #5. When declaring a property for a basic type or for objects you don’t own use the assign attribute.

@interface MyClass : NSObject {
@public int  _counter;
@public NotYourObject* _notMyObject;
}
@property (nonatomic, assign, readwrite) int counter;
@property (nonatomic, assign, readwrite) notMyObject;
@end

Rule #6: When declaring a property for a subclass of NSObject use the retain attribute to retain the object on assignment and release the original object (if any) associated with the property.

@interface MyClass : NSObject {
@public Something*  _thing;
}
@property (nonatomic, retain, readwrite) Something*  thing;
@end

Rule #7: When declaring a property for an NSString use the copy attribute to create a copy on assignment and release the original string (if any) associated with the property. You probably want the assigned string and the original string to be two independent objects.

@interface MyClass : NSObject {
@public NSString*  _moniker;
}
@property (nonatomic, copy, readwrite) NSString* moniker;
@end

Rule #8: Use the autorelease to automatically release objects when you are done with them.

self.thing =  [[[SomeThing alloc] init] autorelease];

Rule #9: Apple API calls that begin with “init” or “copy” need to be managed by you and released when you’re done with them. You can use rule #8 to make releasing objects at the end of a method painless.

self.moniker = [NSString stringWithFormat:@"%@ number %i", @"Betty, 2]; // already autoreleased
self.moniker = [[NSString initWithFormat:@"%@ number %i", @"Betty, 3] autorelease]; // needs autorelease

Rule #10: Always use the nonatomic attribute when declaring properties unless you are working with multiple threads–and if you are working with multiple threads you are way above the level of these rules.

@property (nonatomic, assign, readwrite) int counter;

Rule #11: Always use the ivar in your delloc method to release objects you own and set them to nil (to ensure safe subclassing).

[_moniker release];
_moniker = nil;

A thought on static analysis…

Xcode’s Build and Analyze (Shift-Cmd-A) always drives me crazy with false positives. First, it finds potential issues with the Cocos2D framework source files and I’m not about to worry about or fix any of those. Second, all the issues it finds in my source files are either trivial non-issues (an assignment hidden inside an if-then-else block) or not problems at all (releasing object that it thinks I don’t own). But perhaps it’s my coding style that needs to improve. I hate letting warnings go by unresolved and as soon as figure out a way to write code the makes static analysis happy I’ll let you know.

One reply on “Objective-C Memory Management For Newbies”

Comments are closed.