views:

90

answers:

2

Hi guys,

i have some questions about objective-c's memory management,

let's say:

NSString * test= [[NSString alloc] init ]
test=@"msg";
[object setStr1: test ]; // declared as: @property(copy, readwrite)
[object setStr2: test ]; // declared as: @property(retain, readwrite)
[object setStr3: test ]; // declared as: @property(assign, readwrite)

test=@"some other string"

I think str1 will have a copy of tests content: str1 will point to one adress of the memory (heap) that contais msg, this address is not the same pointed by test. right?

about str2:
1. what does it store?, i guess the same address that points test, but it will increase the reference counter of test to 2.
2. when i change the test's content, what does str2 have? I guess it still points to msg

about str3: it's incorrect, right?, what does assign do?

thanks.

bonus question:

NSString * test= [[NSString alloc] init ]
test=@"msg";
test=@"something";

should i release test before changing its content?

+3  A: 

The most important thing to take away here: The assignment operator = never mutates (i.e. changes) an object. Mutating an object can only be accomplished by sending it messages (e.g., sending appendString: to an NSMutableString). The assignment operator simply causes a pointer to point to a different object than it did before.

Thus, it is incorrect to say:

(1) NSString * test = [[NSString alloc] init];
(2) test = @"msg";

Line (1) creates an NSString object, and assigns test to point to it. Line (2) does the same thing: it creates a new, unrelated NSString object, and assigns test to point to it. Now the original NSString created by line (1) has nothing pointing to it, and is leaked.

Also, you never need to alloc a string literal; the compiler does this implicitely when you use the @"..." syntax. In general, you will very rarely have to use [NSString alloc] at all (only when you want to use the various init* methods, such as initWithFormat:, etc.)

  1. str1 will point to a distinct copy of the test string. (Errata: According to Eiko, the receiver will simply treat this as a 'retain' if it is immutable. This makes no practical difference if you are behaving correctly.)
  2. str2 will point to the same location as test, and the retain count of the object there will be incremented.
  3. str3 will point to the same location as test, but the retain count will not be incremented.

Generally speaking, strings are immutable, so you cannot change their content. You may have to watch out for instances of NSMutableString, however, which is a subclass of NSString. This is why many people recommend copying strings instead of retaining them, so that, should the string be mutated by another part of the program, your object's copy will be unaffected.

David M.
That means if test was a `NSMutableString` and you’d change its content `str1` will still read the original value while `str2` and `str3` will show the new value.
Sven
@David: Number 1 is wrong (see my answer)
Eiko
@Sven: This is correct.
Eiko
I have edited my answer to more clearly spell out the depth of the problem here, Eiko, having not seen your answer in the meantime.
David M.
The secret `retain` used by NSString's `copy` is an implementation detail. Since NSString is immutable, there is no point in allocating another instance. For most classes, it would make a true copy, and can be treated that way even with NSString.
Chuck
so, what will `- (NSString *) stringByAppendingFormat:(NSString *)format ...` return?
jhon
@Chuck - It is mentioned that this detail shouldn't matter.
David M.
@jhon - It will return a new, autoreleased string, leaving the receiver (the string you invoked the method on) untouched. All methods whose names sound like a description of an object behave this way.
David M.
+1  A: 

With your second line you already leak memory, because you reassign test to a new object and lose the reference to the object you created in your first line.

Your conclusion to str1 is wrong, because the copy might just return self for immutable types (they don't change anyway, so often the system is smart enough to keep them around just once).

str2 will indeed point to the same object and just increment the retain count. You cannot change test's content, as it is immutable. If it was an NSMutableString, then yes, str2 would show this change, too.

assign for str3 will just "copy the address", so it points to the same object (as str2), but it does not retain it, so it doesn't claim any ownership/interest in that object. If you release it elsewhere, str3 will point to dead memory.

Bonus: As in my introduction, yes, you leak. Assigning @"msg" makes you leak the original object, as @"msg" will create a new one.

Eiko
No, @"msg" won't be autoreleased, but it is a constant and therefore does not need to be released.
FRotthowe
where is stored @"msg", in the heap?
jhon
@jhon, those strings literals are stored in the data segment of the binary. From there they get loaded into memory (just as the code is) when the program starts. This is not the heap.
Sven
Thanks, removed that sentence.
Eiko
Although NSString literals are not stored on the heap, this makes little practical difference to the code you write.
Chuck
@sven: ok, but then, what happen if i have something like this: `NSString * name=@"abc"; name=someTextField.text;` ?. I think that `abc` will be stored on the data segment as a constant. but, what about someTextField.text (a user wrote its name in the text field)? where is stored that data?
jhon
@jhon: So `name` first points to the `@"abc"` string literal which lies somewhere in the data segment of the program. And after the second statement `name` points to a different `NSString` object which probably lives somewhere on the heap. But you cannot know this for sure, and it does not matter. Just use `retain` and `release` in the right places and you don’t have to worry about any of this.
Sven