I understand that any init... method initializes a new object and that NSString stringWithString makes a copy of the parameter string as a new object. I also understand that
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
See https://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa for some reasons as why you should use auto-release vs release.
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
What? No.
The rules are simple:
alloc
, copy
, copyWithZone
, or new
has a retain count of 1.retain
increases the receiving object's retain count.release
decreases the receiving object's retain count.autorelease
tells the current autorelease pool to send the receiving object the release
message “later”.stringWithString:
) returns an object that it has autoreleased on your behalf.Or, digested a bit:
copy
, alloc
, retain
, or new
returns an object that you own.The incorrect implementation of setName:
that you show is incorrect because it stores an autoreleased object in an instance variable, when you mean to own the object. You should retain it or, in this case, copy it. One way is to simply use alloc
and initWithString:
, as in the correct example you show; the other way would be copy
.
The Memory Management Programming Guide for Cocoa explains everything. Every Cocoa or Cocoa Touch programmer should read or re-read it from time to time.
What I don't understand is when would I use the stringWithString method since any local variable assigned that way would have it's memory "owned" by NSString instead of the local class.
A string created with stringWithString:
isn't owned by the NSString
, it is owned by the NSAutoreleasePool
(although multiple places can retain
an object, making ownership shared).
With stringWithString:
, the string will become invalid when the autorelease pool is next processed (normally during the application's next event loop) because the NSAutoreleasePool will release
its pointer. If you have not retain
ed the string before then, any pointer you have to it (name
in the case of your class) will be invalid (the variable name
will still exist but it will point to garbage).
Autorelease is good, if you don't intend to keep any pointers to the NSString
but since you do intend to keep a pointer, you'll need to retain
the NSString
. initWithString:
gives you a retain count of 1 automatically.
In the Incorrect code above, the next time name is referenced after setName is called, you'll get an exception error, since the object will have been released. You can use either the "Correct" code, or wrap your stringWithString call in an explicit retain call:
name = [[NSString stringWithString: theName] retain];
Actually, both setters are wrong. The 'incorrect' one is wrong for general memory management reasons (which are well-expounded elsewhere). The 'recommended' one is wrong for 2 reasons:
The 'correct' (IMHO) method is:
-(void) setName: (NSString *) theName
{
if (theName == name) return; // if they're equal, no need to do anything further
[name release];
name = [theName copy]; // sets name to nil if theName is nil
}
For most objects you'll actually want to -retain instead of -copy on that third line, but for strings it's almost always better to copy.