NSArray NSString memory leak - objective-c

I have a simple method to read a string and parse it to an array,
-(NSArray *) readFileToArray: (NSString *)file{
NSString *values = [NSString stringWithContentsOfFile: file];
NSArray *tokens = [values componentsSeparatedByString:#":"];
return tokens;
}
however instruments did report me I got a leak on NSString at line
NSArray *tokens = [values componentsSeparatedByString:#":"];
I have no idea why this happens,
1). I think both values and tokens are autoreleased? Am I right?
2). I tried to release values and tokens(just a try), it crashes.
Thanks for your help in advance.
Michael

The code you've posted is using correct memory management (the return value is autoreleased). Look at the code that is calling readFileToArray: to see how it's handling the returned array.

The line that is leaked is NSString *values = [NSString stringWithContentsOfFile: file];
You need to add autorelease in this line to fix the leak.

Related

[__NSCFString count]: Unrecognized selector

I know this has been asked before, but there is no answer that I have found useful.
First off here is my code
// load the .csv file with all information about the track
NSError *error;
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"file" ofType:#"csv" inDirectory:nil];
NSString *datastring1 = [NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:&error];
NSArray *datarow = [datastring1 componentsSeparatedByString:#"\r"];
//fill arrays with the values from .csv file
NSArray *data_seg = [datarow objectAtIndex:0]; //segment number
NSArray *data_slength = [datarow objectAtIndex:1]; //strait length
NSArray *data_slope = [datarow objectAtIndex:2]; //slope
NSArray *data_cradius = [datarow objectAtIndex:3]; //circle radius
NSArray *data_cangle = [datarow objectAtIndex:4]; //circle angle
NSLog(#"%i", [data_seg count]);
Okay, so there is the code, and I read that is has something to do with autorelease, but I was not able to add a retain like NSArray *data_seg = [[datarow objectAtIndex:0] retain]
When I run the code, I get [__NSCFString count]: unrecognized selector sent to instance 0x9d1ad50
Any help is appreciated, I'm not good at programming, and I am very new.
componentsSeparatedByString method returns an NSArray of NSString. Every item that you extract from datarow array is an NSString and an NSString doesn't respond to 'count'. Your code starting at //fill arrays is incorrect. Every objectAtIndex call will return an NSString*.
This is another way of saying that the datatype for data_seg is NSString* (not NSArray*).
With the corrected code snippet, the problem is because data_seg is a string, and -count is not a method of NSString. It seems you think data_seg is an NSArray.
Look at the documentation for -[NSString componentsSeparatedByString:] and see what it returns -- strings! So you get back an array of strings. So what you want is:
NSString *data_seg = [datarow objectAtIndex:0]; //segment number
NSLog(#"my segment number is: %#", data_seg);

problems during object release

I have some problems during when and which object to be release
You can say my knowledge towards this is less
i have following conditions please suggest me the answer accordingly
situation-1
NSMutableString *str=[[NSMutableString alloc]initWithFormat:#"Hello World!"];
NSMutableArray *array=[[NSMutableArray alloc]init];
[array addObject:str];
Now when i tried to release str then usage of array affected in future...and Vice Versa
Tell me can i release both?
situation-2
NSMutableString *str=[[NSMutableString alloc]init];
str=#"Hello World !";
str=[self getData]; //calling a method which returns a string
[str release];
I think i am creating a memory leak here(how to solve it?)
please clear these situations
in the first situation, you'll need to call [str release]; after adding it to the array, like this:
NSMutableString *str = [[NSMutableString alloc] initWithString:#"Hello World!"];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:str];
[str release];
This way, array holds the only retain call on the string. Once you release array later, you won't have any memory leak issues.
I'm a little confused about the second situation. str here is a pointer. You seem to be assigning three different objects to to same pointer:
NSMutableString *str = [[NSMutableString alloc] init]; //first object
str=#"Hello World !"; //second object
str=[self getData]; //third object
by the time you call [str release], you've created a memory leak because you've lost the first mutable string. The second time you called str =, you lost any way to access that first NSMutableString.
Assuming that you're looking to concatenate all of these (since you chose NSMutableString), you might try this:
NSMutableString *str = [[NSMutableString alloc] init]; //first object
[str appendString:#"Hello World!"];
[str appendString:[self getData]];
[str release];
If the [self getData] method returns an autoreleased string, you'll be fine. If getData returns a retained string (if you used alloc and init), you'll need to assign it to an intermediate pointer and release it after adding it to str.
What is the need of creating the NSMutableString You can directly use NSString for this purpose

stringWithFormat vs. initWithFormat on NSString

I am wondering what differences such as disadvantages and/or advantages there are to declaring an NSString this way:
NSString *noInit = [NSString stringWithFormat:#"lolcatz %d", i];
as opposed to:
NSString *withInit = [[NSString alloc] initWithFormat:#"Hai %d", i];
What was the motivation of putting stringWithFormat instead of just having the initWithFormat way of initializing the string?
stringWithFormat: returns an autoreleased string; initWithFormat: returns a string that must be released by the caller. The former is a so-called "convenience" method that is useful for short-lived strings, so the caller doesn't have to remember to call release.
I actually came across this blog entry on memory optimizations just yesterday. In it, the author gives specific reasons why he chooses to use [[NSString alloc] initWithFormat:#"..."] instead of [NSString stringWithFormat:#"..."]. Specifically, iOS devices may not auto-release the memory pool as soon as you would prefer if you create an autorelease object.
The former version requires that you manually release it, in a construct such as this:
NSString *remainingStr = nil;
if (remaining > 1)
remainingStr = [[NSString alloc] initWithFormat:#"You have %d left to go!", remaining];
else if (remaining == 1)
remainingStr = [[NSString alloc] initWithString:#"You have 1 left to go!"];
else
remainingStr = [[NSString alloc] initWithString:#"You have them all!"];
NSString *msg = [NSString stringWithFormat:#"Level complete! %#", remainingStr];
[remainingStr release];
[self displayMessage:msg];
Here, remainingStr was only needed temporarily, and so to avoid the autorelease (which may happen MUCH later in the program), I explicitly handle the memory as I need it.

Getting an NSString out of an NSArray

I am trying to save and read back some application settings stored as NSStrings in an iPhone app and have been having some trouble.
The code to save looks like:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:accountID];
...
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
And the code to read looks like (accountID is an NSString*):
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
accountID = [array objectAtIndex:0];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
The read code throws an exception because after the array is released the accountID variable also appears to have been released (moving the NSLog call before releasing the array works fine). So I'm guessing that I'm creating a reference to the array instead of pulling out the actual string contained in the array. I tried several things to create new strings using the array contents but haven't had any luck.
You guess is on the right lines although you have a reference to the 0th element of the array not the array. The array consists of pointers to NSString objects. The Strings will get get released when yhe array is released.
You need to retain the element you are using e/g/
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
NSString* accountID = [[array objectAtIndex:0]retain];
...
[array release];
NSLog(#"Loading settings for: %#", accountID);
When you release the array the reference to the accountID will also be released. You need to retain it.
accountID = [[array objectAtIndex:0] retain];
Then obviously at some point you need to release it.
try [accountID retain] before you release the array

Alter NSString to get nothing back

At two stages of my app's runtime, I am sending an NSString to the following method.
I receive no warnings or errors in the code editor or the debugger, but when I NSLog the output (secondString), I only get given the memory address of the object.
Is there something I am not doing right here?
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
[firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
[firstString release];
[secondString release];
}
I'd appreciate any help.
Thanks,
Ricky.
Ugh that code is wrong on so many levels.
Here's a simpler version:
- (NSString*)validateString {
NSString *firstString = [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *secondString = [firstString stringByReplacingOccurrencesOfString:#"&" withString:#"%%26" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [firstString length])];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
}
The construct [NSString stringWithFormat:#"%#", aString] is a pretty useless statement, especially when you have copy available. You also have a memory leak and a crash in your code (you create a copy of a string [+1 retain count], assign an autoreleased string into the same variable [+0 retain count, original string lost and leaked], and then release the autoreleased string [crash when the autorelease pool drains]).
First some comments:
1) Everything after the return will not be executed, so the last to statements are useless (dead code).
2) If not created with +alloc, you can assumed that NSString instances are autoreleased, thus you do not need to send the -release message to firstString.
Edit: As Peter Hosey pointed out, you must however release the string obtained by -mutableCopy.
To answer your question:
-stringByAddingPercentEscapesUsingEncoding: returns a pointer to the newly created instance, so you have to save it.
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
firstString = [firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
[secondString release];
return secondString;
}
You need to use %%26 if you want the string "%26"
your NSMakeRange is backwards
your return is too early, and you don't need to release the strings anyway