I have code like this:
@interface MyClass : CCLayer {
}
@property (nonatomic, retain) CCSprite *spriteName; //retain count = 1
@property (nonatomic, retain) CustomClass *customVariable; //retain count = 1
@end
@implementation MyClass
@synthesize spriteName;
@synthesize customVariable;
//rough init method
-(void)init
{
self.spriteName = [CCSprite spriteWithFileName:@"a.png"]; //retain count = same
self.customVariable = [[CustomClass alloc] init]; //retain count = 2
}
-(void)dealloc
{
[self.spriteName release]; //retain count = 0
self.spriteName = nil;
[self.customVariable release]; //retain count = 1?
self.customVariable = nil;
[super dealloc];
}
I have some questions about this:
1) I have a CCSprite which I've made into a property, however when I assigned it to [CCSprite spriteWithFileName:@"a.png"]
, it is an autoreleased object. But because I have @property (nonatomic, retain) CCSprite *spriteName
, I have to release it yes?
2) For my customVariable, I seem to get an error when I release it in the manner above, but when I changed them to underscores, [_customVariable release]; _customVariable = nil
, I get no error. Why is this and what is the difference between the two?
3) Am I releasing these objects right? I have commented in the retain count which I think I'm having trouble understanding. I know the basic if there's an alloc or retain then I should +1 but I'm confused when it comes to properties.
So the full deal with these iVars goes as follows. The compiler silently treats your code like this:
@interface MyClass : CCLayer {
CCSprite *_spriteName;
}
@property (nonatomic,readwrite, retain) CCSprite *spriteName; //retain count = 1
@end
@implementation MyClass
@synthesize spriteName=_spriteName;
@end
in your code, the 'readwrite' and the iVar's name are implicit (_spriteName), and the compiler generates the appropriate code. You could however have an iVar with a different name to point to the actual CCSprite. Using the self.spriteName semantic, the compiler converts that (auto-generates) the code under the hood. In you example the compiler has generated a setter and a getter for 'self.spriteName' (synthesized) as follows:
-(void) setSpriteName:(CCSprite*) theSpriteName {
// this setter can be quite tricky depending on the @property clause
// but for this example, a simplified setter :
[_spriteName autorelease];
_spriteName = [theSpriteName retain];
}
-(CCSprite*) spriteName {
return _spriteName;
}
so when you use self.spriteName , depending on the context (lhs or rhs), the appropriate 'hidden' routine is called. So the line
self.spriteName = [CCSprite spriteWithFileName:@"a.png"];
actually invokes the generated setter above (setSpriteName). Conversely
CCSprite *tempSprite = self.spriteName;
invokes the generated getter. You are always free to use the 'underlying' iVar.
CCSprite *otherTempSprite = _spriteName; // exactly the same.
be very cautious however about changing _spriteName's content or altering its life cycle, as you could accidentally create either a leak or a zombie, until you fully understand the internals. In dealloc, use
[_spriteName release]; // in dealloc, release immediately.
or
self.spriteName=nil; // will invoke the setter above, and the
// actual release could be delayed
So finally, back to your problem with customVariable. Your code actually becomes
[_customVariable release]; // retain count is 0 !!!
[self setCustomVariable:nil];
which when called will execute :
[_customVariable autorelease]; // bad access : message sent to a deallocated variable
_customVariable=nil;