Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

tankit

macrumors newbie
Original poster
Nov 17, 2008
7
0
Hi there,

I have the following example class. The Apple documentation states that retain, copy and assign attributes to property only affects the setters.

https://developer.apple.com/iphone/....html#//apple_ref/doc/uid/TP30001163-CH17-SW2

However I have noticed the retaincount is increased everytime I make a call to a getter.

#import <Cocoa/Cocoa.h>

@interface Person : NSObject {
@private NSString *name;
@private NSNumber *age;
}

@property (readwrite, retain) NSString *name;
@property (readwrite, retain) NSNumber *age;

+ (id) create: (NSString *) name andAge: (NSNumber *) age ;
- (id)init;
- (void) printState;

@end

with the implementation

#import "Person.h"


@implementation Person

@synthesize name;
@synthesize age;

- (id) init
{
if ( self = [super init] )
{
[self setName:mad:""];
[self setAge:nil];
}
return self;
}

+ (id) create: (NSString *) name andAge: (NSNumber *) age
{
Person* person = [[[Person alloc] init] autorelease];
[person setName: name];
[person setAge:age];
return person;
}

- (NSNumber *) age
{
return self->age;
}

- (void) printState
{

NSLog(@"Name of person is %@ age %@",[self name],[self age]);
NSLog(@"Retain count for name and age is %lu, %lu",[name retainCount],[age retainCount]);

}

- (void) dealloc
{
[self setAge:nil];
[self setName:nil];
[super dealloc];
}

@end

main.m

#import <Cocoa/Cocoa.h>
#import "Person.h"

int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSString *name = [NSString stringWithString: @"Fred"];
NSNumber *age = [NSNumber numberWithInt:30];
Person *person = [Person create:name andAge:age];

[person printState];
[person printState];
[person printState];

[pool release];

}

Nov 18 07:23:22 Byron MyTool[2074]: Name of person is Fred age 30
Nov 18 07:23:22 Byron MyTool[2074]: Retain count for name and age is 2147483647, 3
Nov 18 07:23:22 Byron MyTool[2074]: Name of person is Fred age 30
Nov 18 07:23:22 Byron MyTool[2074]: Retain count for name and age is 2147483647, 4
Nov 18 07:23:22 Byron MyTool[2074]: Name of person is Fred age 30
Nov 18 07:23:22 Byron MyTool[2074]: Retain count for name and age is 2147483647, 5

If I supply an implementation that reflects a simply return:

- (NSNumber *) age
{
return self->age;
}


then output is:

ov 18 07:27:38 Byron MyTool[2122]: Name of person is Fred age 30
Nov 18 07:27:38 Byron MyTool[2122]: Retain count for name and age is 2147483647, 2
Nov 18 07:27:38 Byron MyTool[2122]: Name of person is Fred age 30
Nov 18 07:27:38 Byron MyTool[2122]: Retain count for name and age is 2147483647, 2
... and continues the same


therefore I believe the code generated for a retain or copy for a getter must be sometime like:

- (NSNumber *) age
{
return [self->age retain];
}

Can anyone confirm this behaviour? Should I be concerned or will I have to place an autorelease on every getter call.

(Note: I have disabled GC in Xcode)
 
After thinking about it i believe i shouldn't be worried.

Each object returned from a getter is a super class of NSObject. Since these were created using the in-build methods (not alloc) then they are registered with the auto release pool. When the pool is released then these retain counts are cleared.

Is this correct assumption?
 
To summaries this I believe the generated code for a getter is:

- (NSString *)name {
return [[aName retain] autorelease];
}

This would only apply to assign/retain.
 
My app has a retain property called token. I put in the following code to test your theory and it seems you are right. After I released the autorelease pool, the local value I got was invalid.

Code:
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
    self.token = [NSMutableString stringWithString:@"testing"];
    NSLog(@"ret %d", [self.token retainCount]);
    NSString *str = self.token;
    self.token = nil;
    NSLog(@"%@ %d", str, [str retainCount]);
    [p release];
    NSLog(@"%@ %d", str, [str retainCount]);
 
Does token have a copy or retain attribute for the property? What happens if you changed self.token = nil to [self token] release?

My app has a retain property called token. I put in the following code to test your theory and it seems you are right. After I released the autorelease pool, the local value I got was invalid.

Code:
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
    self.token = [NSMutableString stringWithString:@"testing"];
    NSLog(@"ret %d", [self.token retainCount]);
    NSString *str = self.token;
    self.token = nil;
    NSLog(@"%@ %d", str, [str retainCount]);
    [p release];
    NSLog(@"%@ %d", str, [str retainCount]);
 
Only applicable for multi-threaded code. Wraps the variable in a lock to ensure no other thread modifies the variable during the call. I will give the nonatomic option a go and see what the results are.

Cheers

This is what the nonatomic keyword is for. Check out the Atomicity section.
 
You're right with the retain count increasing as auto generated code is giving you back an autoreleased/retained version for you to work with. Your simple getter is simply returning the reference to the object. Normally for String objects, you would copy, not retain. Even though you believe you are referring to an NSString, it could actually be an NSMutableString, and you would not want the data changing half-way through the app run time.
 
changing the property attributes to

@property (readwrite, nonatomic, copy) NSNumber *age;

with the nonatomic keyword it does change the dynamics. The generated code has removed the retain and autorelease.

[[aName retain] autorelease];

it is now a simple return as the retaincount is not increasing with multiple getter calls. The default is to allow multiple threads to handle the return object without the fear of it been GC'd. Using the nonatomic keyword removes this, however if multiple threads attempted to utilised the return object there could be a risk one of these threads may release this object (bad situation) and the GC will clean the data from the object.

You're right with the retain count increasing as auto generated code is giving you back an autoreleased/retained version for you to work with. Your simple getter is simply returning the reference to the object. Normally for String objects, you would copy, not retain. Even though you believe you are referring to an NSString, it could actually be an NSMutableString, and you would not want the data changing half-way through the app run time.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.