problems during object release - objective-c

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

Related

Trying to build polygon from NSString

So, I'm trying to build an array of CGPoints by breaking an NSString, typically look like this:
31.241854,34.788867;31.241716,34.788744;31.242547,34.787585;31.242661,34.787719
Using this code:
- (NSMutableArray *)buildPolygon:(NSString *)polygon
{
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
NSMutableArray *polygonArray = [[NSMutableArray alloc] init];
for (int i=0; i < polygonArray.count; i++)
{
NSArray *polygonStringArray = [[NSArray alloc] init];
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
CGFloat xCord = [[polygonStringArray objectAtIndex:0] floatValue];
CGFloat yCord = [[polygonStringArray objectAtIndex:1] floatValue];
CGPoint point = CGPointMake(xCord, yCord);
[polygonArray addObject:[NSValue valueWithCGPoint:point]];
}
NSLog(#"return polygonArray: %#", polygonArray);
return polygonArray;
}
But eventually I get an empty array.
What I'm doing wrong?
You're defining polygonArray as an empty array just before the start of your for loop. You should define polygonArray like:
NSArray *polygonArray = [polygon componentsSeparatedByString:#";"];
And you don't even need to bother with that stringArray variable.
You have confusion over alloc & init, and one simple typo...
The confusions first:
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
This creates a new NSMutableArray and stores a reference to it in stringArray. All good so far.
[stringArray addObject:[polygon componentsSeparatedByString:#";"]];
And this obtains a reference to an NSArray ([polygon componentsSeparatedByString:#";"]) and adds it as a single element to the mutable array referenced by stringArray. There is nothing wrong per se with this, but it is not what you want in this case - you just want the array returned by componentsSeparatedByString:. You do this with:
NSArray *stringArray = [polygon componentsSeparatedByString:#";"];
Which takes the reference returned by componentsSeparatedByString: and stores it in the variable stringArray - no alloc or init required as you are not creating the array yourself. You don't even own this array, so if you are using MRC there is no need to release it later.
NSArray *polygonStringArray = [[NSArray alloc] init];
Now this allocates an immutable empty array and stores a reference to it in polygonStringArray. This is not a very useful array, as it contains nothing and cannot be modified! But you don't keep it around long...
polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
This obtains a reference to an array from componentsSeparatedByString: and stores it in polygonStringArray. If you are using MRC this will cause a leak - your pointless zero-length array created above will leak, and a new zero-length array will be created and leaked every time around the loop.
You are confused over allocation - you only need to allocate things you are creating; when you receive a reference to an already allocated object you only need to store that reference. (If using MRC you may also need to retain/release/autorelease it as well - but let's stick with ARC.) So all you needed here was:
NSArray *polygonStringArray = [[stringArray objectAtIndex:i] componentsSeparatedByString:#","];
Now your code is almost correct, just one typo:
for (int i=0; i < polygonArray.count; i++)
Well you are filling polygonArray in this loop and it starts off as empty, what you need is stringArray.count.
HTH

I have a memory leak in this objective-c method, can anyone tell me where?

I'm receiving an exc_bad_access somewhere in the code below. I don't understand where it is if anyone could shine any light on it? It's a method that takes in an NSMutableArray of dictionaries and sorts them by one of the elements in the dictionary. The memory leak is almost certainly in the bit with the block but I think i'm missing something fundamental in finding it...
-(NSMutableArray*)sortBicyclesByDistanceToDevice:(NSMutableArray*)inputArray{
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b){
NSNumber *first = [[a objectForKey:kDistanceFromDevice] objectForKey:kValue];
NSNumber *second = [[b objectForKey:kDistanceFromDevice] objectForKey:kValue];
return [first compare:second];}];
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
[arrayToHoldSorted release];
return [retVal autorelease];
}
Thanks
It looks like you assign retVal to an NSMutableArray through then reassign immediately after. The original alloced NSMutableArray will leak. That is:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
Should be:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
Replace:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
With:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
You are leaking the first value of retVal.
There's more than one in there!
This line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
Is a memory leak since you immediately reassign the pointer. It should be removed. Just declare your array on the next line:
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator...
This method returns an autoreleased object, so you don't need to release it later on.
A similar pattern with the mutable array. You alloc/init, then overwrite with a new object, giving another leak. Again, remove the alloc/init line and just declare in the next line. mutableCopy gives you an implicitly retained object, so you do need to autorelease it.
You seem to be under the impression that alloc/init is needed every time you declare an object variable. This is not the case.
You allocate arrayToHoldSorted (1) - which you never use as you then get an NSArray back from sortedArrayUsingComparator(2). And then you release it afterwards(3) when you don't own it. You do the same trick for retVal, allocating a NSMutableArray - then overwriting your reference to it by getting a new NSMutableArray from [arrayToHoldSorted mutableCopy];
NSArray *arrayToHoldSorted = [[NSArray alloc] init]; .. // 1
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) ..... // 2
[arrayToHoldSorted release]; // 3
Just assign the return NSArray from sortedArrayUsingComparator to a reference...
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) .....
I think the problem is that in this line:
return [retVal autorelease];
you release something that you have not retained. Also in this line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
you have an extra [, which does not help. But most importantly, you can use the static analyzer in XCode to diagnose this sort of bug, rather than pestering the good folk on StackOverflow.

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

Why am I getting an invalid NSString from SQLite for my UILabel?

I have a view with a UILabel and a UITableView. I'm using this code to get a string from a database:
-(void)getOneQuestion:(int)flashcardId categoryID:(int)categoryId {
flashCardText=[[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
NSString *martialStr=[NSString stringWithFormat:#"%d", flashcardId];
NSString *queryStr=[[NSString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo where flashCardId="];
queryStr=[queryStr stringByAppendingString:martialStr];
NSString *martialStr1=[NSString stringWithFormat:#"%d", categoryId];
NSString *queryStr2=[[NSString alloc] initWithString:#" and categoryId="];
queryStr2=[queryStr2 stringByAppendingString:martialStr1];
queryStr=[queryStr stringByAppendingString:queryStr2];
unsigned int lengthOfString=[queryStr length];
char temp2[lengthOfString +1];
strcpy(temp2, [queryStr cStringUsingEncoding:NSUTF8StringEncoding]);
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:temp2];
while(sqlite3_step(dataRows) == SQLITE_ROW) {
flashCardText =[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,0)];
flashCardAnswer=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)];
flashCardTotalOption=sqlite3_column_int(dataRows,2);
}
sqlite3_reset(dataRows);
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
When I click on the table cell, the string value (flashCardAnswer) shows invalid.
Although this code snippet doesn't seem to show where the string value is assigned to the UI element, it would seem that the problem could stem from your use of +[NSString stringWithUTF8String:] inside the while loop. This returns an autoreleased string which you much retain if you want to use it outside the scope of the method. Since those appear to be instance variables that you use in another part of the code to change the UI, you have a few options:
Send a -retain to each of them before exiting the method.
Use +alloc and -initWithUTF8String:.
Use a setter method or property that takes care of the details for you. (Thanks, Chuck!)
As a bonus, I have a few other related suggestions.
You're leaking memory by allocating strings for flashCardText and flashCardAnswer at the start of the method, since you overwrite them in the while loop.
Use -[NSString getCString:maxLength:encoding:] to write the query string into a char* buffer without the strcpy() call, or just use the char* from -cStringUsingEncoding: directly.
There is a lot of potential for simplifying the construction of your query string — definitely investigate NSMutableString. For example...
NSMutableString* query = [[NSMutableString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo"];
[query appendFormat:#" where flashCardId=%d", flashcardId];
[query appendFormat:#" and categoryId=%d", categoryId];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:[query cStringUsingEncoding:NSUTF8StringEncoding]];