views:

8099

answers:

9

I have the following example class:

Test.h:

@interface Test : UIButton {
    NSString *value;
}
- (id)initWithValue:(NSString *)newValue;
@property(copy) NSString *value;

Test.m:

@implementation Test
@synthesize value;
- (id)initWithValue:(NSString *)newValue {
    [super init];   
    NSLog(@"before nil value has retain count of %d", [value retainCount]);
    value = nil;
    NSLog(@"on nil value has retain count of %d", [value retainCount]);
    value = newValue;
    NSLog(@"after init value has retain count of %d", [value retainCount]);
    return self;
}

Which produces the following output:

2008-12-31 09:31:41.755 Concentration[18604:20b] before nil value has retain count of 0
2008-12-31 09:31:41.756 Concentration[18604:20b] on nil value has retain count of 0
2008-12-31 09:31:41.757 Concentration[18604:20b] after init value has retain count of 2147483647

I am calling it like:

Test *test = [[Test alloc] initWithValue:@"some text"];

Shouldn't value have a retain count of 1? What am I missing?

Thanks for your help.

+9  A: 

You are passing in a literal string. The compiler probably allocates it in static memory and sets the retain count to the maximum possible value.

Try a dynamically allocated string instead and see what happens.

NSString* string = [[NSString alloc] initWithString: @"some text"];
Test* test = [[Test alloc] initWithValue: string];
Stefan Tannenbaum
it's allocating it in static memory even though i use the copy attribute on the property?
If you want to use the property, you have to say "self.value = whatever" instead of just "value = whatever", which simply assigns the instance variable.
Kristopher Johnson
done.. same error. i've also tried using the dynamic string code above.
+3  A: 

You're passing in a string constant, which can't really be deallocated. I think that 2147483647 is probably UINT_MAX, which basically means that the object can't be released.

Stephen Darlington
doesn't the copy attribute make a new copy not in static memory?
What would the point of that be? You can't change the value, so you may as well point at the static memory. Maybe worth trying changing the type to NSMutableString to see if you get the same behaviour.
Stephen Darlington
it's possible for a -copy to actually -retain the original when the original is immutable, so a -copy would be guaranteed identical.
Graham Lee
+1  A: 

I think you want to do this:

self.value = newValue;

which will invoke the property setter and cause the copy to occur. "value = newValue" simply assigns a pointer value to the instance variable.

Kristopher Johnson
thanks,.. i made the change.. still the same error
A: 

hmm.. we're getting closer.

it appears that newValue's retain count is also 2147483647.

I tried dynamically allocating the string instead with the same retain count results.

I found a helpful article here: http://www.cocoadev.com/index.pl?NSString

FTA:

Does the NSString returned by @"" need to be released, or is it autoreleased? Neither. @""-strings are of class NSConstantString?, and thus act like atoms in lisp; they hang around. That is, if you use @"cow" in two separate places in your code, they will be referencing the very same object. I don't think -release or -autorelease does anything to either of them.

If I have "copy" on the property though, shouldn't it copy the contents of the target memory into new memory with a retain count of 1? It would seem the copy attribute does nothing in this case?

This is my understanding: When you do a "copy" on an immutable object, you actually just retain it. Doing a copy of a mutable string (or other mutable object) actually does copy it.Don't get too hung up on the whole retainCount thing. If you balance every alloc with release you will do fine.
Chris Lundie
+1  A: 

You've got a reference to an immutable string. Assignment doesn't need to copy the value (the string data) since it's immutable. If you do a mutable operation, like value = [newValue uppercaseString] then it should copy the bits into value, and value's retain count incremented.

This doesn't really address the fundamental issues with the original poster's code: He's not following the memory management rules, and he's assigning directly to an instance variable instead of going through the property.
Chris Hanson
+11  A: 

Don't look at retain counts. They're not useful and will only mislead you — you can't be certain that nothing else is retaining an object, that an object you get from somewhere isn't shared.

Instead, concentrate on object ownership and follow the Cocoa memory management rules to the letter. That way your memory management will be correct no matter what optimizations Cocoa may be doing behind the scenes for you. (For example, implementing -copy as just -retain for immutable objects.)

Furthermore, it's critical to understand the difference between properties of your objects and instance variables within your objects. In your question's code, you are assigning a value to an instance variable. That instance variable is just that: a variable. Assigning to it will behave like any other variable assignment. To use the property, you must use either dot syntax or bracket syntax to actually invoke the property's setter method:

self.value = newValue;     // this is exactly equivalent to the next line
[self setValue:newValue];  // this is exactly equivalent to the previous line

The code generated for the dot syntax and the bracket syntax is identical, and neither will access the instance variable directly.

Chris Hanson
+2  A: 

You shouldn't be paying attention to the retain counts, just follow the Cocoa memory management rules. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html

Graham Lee
A: 
#import <Foundation/Foundation.h>

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

    char *cstr = "this is a c string";

    NSString *str = [[NSString alloc] initWithUTF8String:cstr];
    NSLog(@"rc1: %d", [str retainCount]);

    [pool drain];
    return 0;
}

If you run the above code, it will display a retain count of 1

Greg Skluzacek
A: 

In Cocoa, many immutable objects will simply retain themselves when you ask for a copy within the same zone. If the object is guaranteed not to change (i.e. its immutableness) then an exact duplicate is redundant.

In Objective-C, the constant string class is separate to Cocoa's NSString class, although it may be a subclass of NSString (I'm not too sure). This constant string class may override NSObject's methods like retain, release and dealloc so that they do nothing, and also override retainCount so that it always returns the same number, UINT_MAX or so. This is because an Objective-C constant string is created in static memory. It must have the overall general behaviour of a Cocoa object (when using Cocoa) so that it can be added to arrays, used as keys to a dictionary etc, except in regards to its memory management, since it was allocated differently.

Disclaimer: I don't actually know what I'm talking about.

dreamlax