NSString copy or alloc? Do you feel lucky? - objective-c

I have a thread that needs information from the GUI before starting. What I mistakenly tried to do at first was create pointers to the NSTextFields like so:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
This caused problems when I tried to manipulate "info" from within the thread. I assume this is the case because technically, it was still pointing to the string representation of the NSTextField outside the thread.
This fixed the problem:
NSString *info = [[gui_field stringValue] copy];
I assume this made a copy (with its own memory space) that did not rely on the NSTextField at all. I also assume this should be thread-safe.
Is this the appropriate way to do this? I suppose I could have done this:
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Are the two producing the same result? And do I have to explicitly call release on the string when using "copy" or is it autoreleased by default?
Update: or, perhaps I could just send a pointer to the thread, and copy the string with "autorelease," adding it to the thread's autorelease pool:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
//do stuff
[pool drain];
//copied_str should now be history
}
This way, I don't have to worry about explicitly releasing copied_str. It will be gone once the thread ends.

No need to rely on luck :)
alloc, copy, new and mutableCopy mean you own the object. Both of those will give you a retained object. If you're managing memory, you need to release it.
By convention, other methods will give you an autoreleased object.
As an example, if you want an autoreleased object, you can call:
NSString *str = [NSString stringWithString:yourString];
See the memory management guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
Specifically the four rules here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
Finally, both will copy the string.
from the NSString docs:
initWithString:
Returns an NSString object initialized by copying the characters from another given string.
copy is from NSObject. It defines copy as:
Return Value
The object returned by the NSCopying protocol method copyWithZone:, where the zone is nil.
NSString implements the NSCopying protocol so copy will return a copy of the string.
There is one exception where the string isn't copied by initWithString - if you pass a string literal it will wrap the pointer to the constant and ignore retain/release. See here if you're curious: Difference between NSString literals

NSString *info = [[gui_field stringValue] copy];
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Those do pretty much the exact same thing.
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
No, you can't do that. Even accessing the GUI string object just to copy it could be enough for a crash.
I think this is a case where the usually recommended patterns for memory management really don't provide a great solution, so you can go outside them.
NSString *info = [[gui_field stringValue] copy];
//launch thread
//pass ownership to callee
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
// myMethod owns info!
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[info autorelease];
Another option is retain infoCopy as an instance variable somewhere. That would let you use normal memory management patterns, but it doesn't fit semantically. It's really not an instance variable, it's an argument.
"I asked for an argument."
"This is abuse."

Related

Is this a correct situation to call release on an object in Objective-C?

I'm new to Objective-C and I am having some difficulty with understanding memory management.
So let's say I have a class and an instance variable of type NSString* that isn't tied to a property. Would this situation leak memory?
myString = [[NSString alloc] init];
//more program stuff
myString = [[NSString alloc] init];
Should I have called [myString release] before I set myString equal to a new object? Will this sort of code leak memory? Do I have to release an object like this every time I have the pointer point to a different object?
First, Apple's Memory Management Programming Guide is a great place to look for examples and instructions about memory management.
Now to your question. When you call myString = [[NSString alloc] init]; you are reassigning the pointer myString and as such lose access to the original NSString, thus creating a memory leak.
The general rule of thumb here is that for every alloc you should have a release and these must alternate appropriately. If you do
myString = [[NSString alloc] init];
// ...
myString = [[NSString alloc] init];
// ...
[myString release];
[myString release];
you are releasing the same instance twice which results in overreleasing and an error BAD-ACCESS. The correct thing to do is
myString = [[NSString alloc] init];
// ...
[myString release];
myString = [[NSString alloc] init];
// ...
[myString release];
so that each instance is correctly released.
For future, stick to the simple rule that for every alloc/copy/retain you should pair it with a release.
Yes this will leak.
By allocating and assigning another NSString instance to the same variable myString, you lose reference to the original content of myString and therefore the ability to send it a release message.
Both, the Clang static analyzer and the Leaks instrument should uncover this leak.
Yes, you need to release the first object before you reassign your variable to point to the second object. Otherwise, you lose track of your first object and you leak it because you can't release it. You are responsible for releasing the object because you created it (via alloc) as explained in the Memory Management Rules.
Also, bear in mind that [[NSString alloc] init] is creating an empty and immutable string (NSStrings are immutable meaning that they can not be changed after creation). There is little point in doing this.

autoreleasing NSString in class method causing app crash in iOS

The error I receive is as follows:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); //breakpoint that says Thread 1: Program Received Signal: "EXC_BAD_ACCESS".
[pool release];
return retVal;
}
My two questions can be found at the bottom of this post :)
I am currently working on an assignment for an iOS programming class and have hit a road bump.
I have found a fix, shown below, but it doesn't make sense to me. Check it out:
#implementation MyClass
// This class method takes an (NSMutableArray *) and returns an NSString with its contents printed out.
+ (NSString *)myString:(NSMutableArray)anArray
{
// NSString *myString = [[NSString alloc] init]; OLD CODE THAT CAUSES MEMORY LEAK
NSString *myString = [[[NSString alloc] init] autorelease]; //NEW CODE THAT RELEASES FIRST ALLOCATION OF myString WHEN THE FIRST stringByAppendingFormat: IS CALLED
NSString *vp = VARIABLE_PREFIX; //#defined above to be #"%
for (id object in anArray) {
if ([object isKindOfClass:[NSString class]]) {
if ([object hasPrefix:vp]) {
myString = [myString stringByAppendingFormat:#"%#",[object substringFromIndex:1]];
}else{
myString = [myString stringByAppendingFormat:#"%#",object];
}
}else if ([object isKindOfClass:[NSNumber class]]) {
myString = [myString stringByAppendingFormat:#"%#",object];
}
}
return myString; //shouldn't I autorelease myString right before this line? NO NOT ANY MORE. THIS myString IS NOT THE ORIGINAL THAT I alloc-init, BUT AN AUTORELEASED OBJECT RETURNED BY THE stringByAppendingFormat: message.
}
When I try to send the message [myString autorelease];, the program crashes with the above error. It is working fine now as shown above, but I do not understand why.
Every time I send a message containing the "magic words" alloc, init, copy I have to call release, it don't I? Or are the rules different in a Class method (can the Class itself own a file?). I do not call retain in the object that is calling this file.
Here are my two questions:
Why does this crash when I try to release theDescription using autorelease?
Does my code create a memory leak?
This is my very first question on stack overflow! Thank you for your help!
Why does this crash when I try to release theDescription using autorelease?
Assuming you mean myString, it crashes because myString is already autoreleased. You got it by calling -stringByAppendingFormat:, which returns an autoreleased string. Now, you're probably thinking: "But I created it by calling +alloc, so I should release it." That's true, but NSStrings are immutable, and when you call -stringByAppendingFormat: you get a different string back, and that string is autoreleased. Autoreleasing it a second time is an error.
Does my code create a memory leak?
Yes, but not really. The "leaked" object is the empty string that you allocate in the beginning. You never release that string, so you've got a leak. However, NSString is apparently optimized so that [[NSString alloc] init] returns a singleton, so in this particular case it doesn't make any difference that the empty string isn't released. The other strings that are assigned to myString are all autoreleased, so none of those objects are leaked.
Why does this crash when I try to release theDescription using
With the updated code, the problem is that ypu are reassigning the pointer to myString using the methid which already returns an autoreleases object: stringbyappending, therefore if you call autorelease on this object which is already going to get autoreleased i will crash.
Aditionaly the first assugnment in the alloc init gives a memory leak when ypu reassign with stringbyappendingstring, since you lose the reference to the previously created string with alloc init and therefore you will never be able to release it.
Sorry for the formatting on my iPhone atm =)

Memory leak for NSDictionary loaded by plist file

I have a memory leak problem that just can not understand! Watch this initialization method:
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}
return self;}
Then this little variation (note self.tipologia):
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:#"Test"];
}
return self;}
In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:
[NSDictionary dictionaryWithContentsOfFile:pathOpere]
This is the dealloc method:
- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}
Remember that alloc returns an object that you own.
If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.
If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.
Either way, that is your leak.
The property should be declared as copy; if it already is, don't try to fix the leak by changing that.
You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.
So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.
You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.
When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.
The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.
Try
nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
or
self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];
instead of self.xxx = [[yyy alloc] init...].
In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the #property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.
BTW, there's no point calling +dictionaryWithDictionary:. Just use
NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera];

NSArray acts weirdly with objects going out of scope

I have a weird problems with NSArray where some of the members of the objects in my array are going out of scope but not the others:
I have a simple object called Section.
It has 3 members.
#interface Section : NSObject {
NSNumber *section_Id;
NSNumber *routeId;
NSString *startLocationName;
}
#property(nonatomic,retain) NSNumber *section_Id;
#property(nonatomic,retain) NSNumber *routeId;
#property(nonatomic,retain) NSString *startLocationName;
#end
#implementation Section
#synthesize section_Id;
#synthesize routeId;
#synthesize startLocationName;
//Some static finder methods to get list of Sections from the db
+ (NSMutableArray *) findAllSections:{
- (void)dealloc {
[section_Id release];
[routeId release];
[startLocationName release];
[super dealloc];
}
#end
I fill it from a database in a method called findAllSection
self.sections = [Section findAllSections];
In find all sections I create some local variables fill them with data from db.
NSNumber *secId = [NSNumber numberWithInt:id_section];
NSNumber *rteId = [NSNumber numberWithInt:id_route];
NSString *startName = #"";
Then create a new Section and store these local variable's data in the Section
Section *section = [[Section alloc] init];
section.section_Id = secId;
section.routeId = rteId;
section.startLocationName = startName;
Then I add the section to the array
[sectionsArray addObject:section];
Then I clean up, releasing local variables and the section I added to the array
[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
In a loop repeat for all Sections (release local variables and section is done in every loop)
The method returns and I check the array and all the Sections are there. I cant seem to dig further down to see the values of the Section objects in the array (is this possible)
Later I try and retrieve one of the Sections
I get it from the array
Section * section = [self.sections objectAtIndex:row];
Then check the value
NSLog(#" SECTION SELECTED:%#",section.section_Id);
But the call to section.section_Id crashed as section.section_Id is out of scope.
I check the other members of this Section object and they're ok.
After some trial and error I find that by commenting out the release of the member variable the object is OK.
//[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
My questions are:
Am I cleaning up okay?
Should I release the object added to an array and the local variable in the function?
Is my dealloc okay in Section?
Does this code look ok and should I be looking elsewhere for the problem?
I'm not doing anything complicated just filling array from DB use it in Table Cell.
I can comment out the release but would prefer to know why this works, and if I shouldn't be doing this. The only place that secId is released is in the dealloc.
You should not be releasing secId, rteId, or startName. secId and rteId are pointers to NSNumber instances created with a factory method that returns an already-autoreleased object. Static strings (i.e. #"") do not need to be released. You need to re-read the Memory Management Programming Guide. Then read it again ;-) It will be your friend.
You're releasing objects you don't own. You should read the memory management rules.
I'll second (third) the suggestion to read the memory management rules.
The TL;DR version is anything you alloc and call a method with init in the method name on is your responsibility to release. For instance:
NSString *string = [[NSString alloc] initWithFormat:#"%#", someObject];
In this case you must release string. However:
NSString *string = [NSString stringWithFormat:#"%#", someObject];
Here string is autoreleased. It's basically equivalent to this:
NSString *string = [[[NSString alloc] initWithFormat#"%#", someObject] autorelease];
...meaning that the next time through the event loop (which means possibly as soon as your function returns), the system will send a release message to it for you. Apple calls these "convenience methods".
If you have something like this:
NSString *string = #"foo";
Then string is pointing to an instance of NSString that is created by the runtime when your program initializes and won't go out of scope until your program terminates. Never release these either.
Again, read the guidelines and bookmark them. But this should answer your direct question.

Releasing of strings

I got a doubt that when should the strings are released.I am not made any allocation to the string is there any necessary to release the string?
No if you do not "allocate" the string they are auto released.
for example
NSString *aTestString = [NSString stringWithFormat:#"Hello %#",#"World"];
This string is auto released, so you do not have to call [aTestString release];
If you would do:
NSString *aTestString = [[NSString alloc] initWithFormat:#"Hello %#",#"World"];
Then you would need to release it by [aTestString release]; because you manually allocated.
Therefore it is wise to autorelease it, so you do not have to think of it later on
NSString *aTestString = [[[NSString alloc] initWithFormat:#"Hello %#",#"World"] autorelease];
But that would just be the same as the first piece of code I gave ya.
Back to the point, no you do not have to manually release it as long as you do not allocate it yourself.
Did you create the string via a call to alloc, new, or a method containing copy? Did you explicitly retain the string yourself? If you got the NSString from a CFStringRef, did you create the CFStringRef with a function that included create? If not, you don't have to do anything. If you did, you have to either release or autorelease the string.
Object allocation/deallocation rules
You need to call [Object release] if and only if:
You called [Object alloc]
You called [Object retain]
You called [Object new]
If you did not explicitly allocate or retain the object, then you need to release it. If you got the object via a class method, the method did something like this: return [[[Object alloc] init] autorelease];. This allocates a new object, but is autoreleased when the NSAutoReleasePool next gets a chance.