Question

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.

Was it helpful?

Solution 5

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.

OTHER TIPS

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.

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

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.

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.

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

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?

#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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top