1
votes

Could someone please help me understand reference counting in Objective C properties.

Suppose I have class

@interface TA : NSObject
{
    TB* tb;
}
- (id) init;
- (void) dealloc;
@property (nonatomic,retain) TB* tb;
@end

@implementation
@synthesize tb;
- (id) init {...}
- (void) dealloc {...}
@end

My understanding is that assignment of new value to "tb", such as "ta.tb = newValue" is equivalent to the following logic:

if (newValue != oldValue)
{
    [newValue retain];
    [oldValue release];
    tb_storage_cell = newValue;
}

But how does it work inside the init method?

Does [TA alloc] pre-initialize instance memory with zeroes?

Do I need to execute tb = nil inside init?

If alloc does pre-initialize memory with zeroes, it appears setting tb = nil is not necessary inside the init since tb is already nil. Is that right?

On the other hand if alloc does not zero out allocated memory, and it contains garbage, then an attempt by the setter to release old value inside the initializing assignment should crash and it may never work. So does it mean that alloc is indeed guaranted to return always zeroed-out block of memory?

Next, to dealloc.

Supposed sequence is inside dealloc is:

[tb release];
tb = nil;
[super dealloc];

Is that right?

But if so, how again does it work? First release is supposed to release "tb". Then the assignment "tb = nil" is supposed to release tb's oldValue again, so it should amount to double release and crash...

Or am I supposed to skip "[tb release]" inside the dealloc and simply do

tb = nil;
[super dealloc];

?

2

2 Answers

3
votes

The Objective-C spec explicitly states that all object instances have their members zeroed out when allocated.

A property's get and set methods are only invoked when you use instance.property syntax. Your "tb = nil" line is simply setting the instance variable's value to nil, not invoking the property.

You would have to do self.tb = nil to invoke the property setter. You should generally always use the property syntax when releasing values in the dealloc method.

self.tb = nil;

This will properly release and nil out the property.

0
votes

You could write your methods as:

-(id) init{
if(self=[super init]){
tb = [[TB alloc] initWithSomething];
}
return self;
}

- (void) dealloc{
[tb release];
[super dealloc];
}
- (void) someMethod{
NSLog(@"This is ok since tb is always initialized. %@", [tb description]);
NSLog(@"This is also ok. %@", [tb description]);
}

This is a typic initialization, or if you think that tb does not have to be initializaed from the beggining, then you could make it lazy:

-(tb) tb{
if (!tb)
   tb = [[TB alloc] initWithSomething];
return tb
}

 -(id) init{
    self=[super init];
    return self;
    }

- (void) dealloc{
[tb release];
[super dealloc];
}
- (void) someMethod{
NSLog(@"This might be not ok, tb is not necessarily initialized:%@ ", [tb description]);
NSLog(@"This is ok since tb is always initialized by the getter. %@", [self.tb description]);
}

By doing this you will have to call tb using properties to ensure is initialized, unless you initialized it in some other part of your code.