Search code examples
objective-cmemory-managementcocos2d-iphonedealloc

Difference between self.variableName and _variableName


I have code like this:

MyClass.h

@interface MyClass : CCLayer {

}

@property (nonatomic, retain) CCSprite *spriteName;  //retain count = 1
@property (nonatomic, retain) CustomClass *customVariable; //retain count = 1

@end

MyClass.m

@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.


Solution

  • 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;