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;

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

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 = 10;

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

Rectangle* rect = self.rectangle; = 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;

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;

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;

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.

Cocos2d-iPhone Programming

Cocos2d Tip #1: Changing a Sprite’s Image Simply

I’m writing an iPhone game using the Cocos2d-iPhone framework. It’s been smooth sailing except for one little detail: I want a sprite to change it’s image based on a touch.

I think the problem is that there are a dozen ways to do this in Cocos2d. I wanted to find the simplest way to do it.  A quick Google search pointed me in the direction of CCAnimationCache and CCTextureCache.

CCAnimationCache is used for running multi-frame animations inside a single sprite. I only have 2 frames for my sprite! CCAnimationCache could do it but it’s a more power than I need to respond to a touch. (If you want to give animation a try check out Ray Wenderlich’s wonderful tutorial.)

CCTextureCache would have been the way to go if I hadn’t already used CCSpriteFrameCache to load all my sprite images at the start of my game. CCSpriteFrameCache uses a plist that divides up a large image into rectangle so you can pack all your sprite images into one memory saving “texture atlas.” And you can use Zwoptex to generate the image and the plist for you. CCTextureCache is the manual way to do what CCSpriteFrameCache and Zwoptex automates for you. (If you like to be hard core check out Ben’s post on optimizing texture loading.)

I figured there had to be a way to use my sprite frames in my existing cache without having to resort to manually loading textures or creating animations I didn’t need.

There was! It’s simple and here’s how to do it…

First, create your texture atlas with Zwoptex and add the .plist and .png files to your Resources folder in your Xcode project.

Second, load the texture atlas into the shared frame cache early in your game…

CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:@"textureAtlas.png"];

Third, create your sprite in your layer’s – init method…

// Note: niceSprite is a CSprint* instance variable so we can reuse it
niceSprite = [CCSprite spriteWithSpriteFrameName:@"initial_image.png"];
niceSprite.position = CGPointMake(240, 160);
[self addChild:niceSprite z:0 tag:0];

Fourth, in your – ccTouchBegan or – ccTouchesBegan method add the following code to change the image associated with your sprite…

CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCSpriteFrame* frame = [frameCache spriteFrameByName:@"new_image.png"];
[niceSprite setDisplayFrame:frame];

That was easy!