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

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.

Related

Replace content of NSMutableArray with NSArray

I'm working on an app that needs to retrieve some data from a server. I have created a "Server" class which handles all the communication and has a NSMutableArray *sessionData variable where I would like to store the data coming from the server (btw, is this approach correct?).
I have the data in an NSArray. I would like the NSMutableArray to have the same content of the NSArray but I didn't find any way to do this (sessionData = requestResult).
(subquestion: do I have to initialize in some way the NSMutableArray before using ? I have only declared it with #property and #synthesize)
The code you tried (from the comment) should have worked. The reason it did not work is that your sessionData was nil.
You need to initialize your sessionData - set it to [NSMutableArray array] in the initializer; then your code
[sessionData removeAllObjects];
[sessionData setArray:result];
will work perfectly. You do not even need the first line - the second one replaces the content of sessionData with that of the result.
Try this way:
sessionData = [result mutableCopy];
[result release];
Or
NSMutableArray *sessionData = [[NSMutableArray alloc] initWithContentsOfArray:result];
Or, if you could do this:
NSMutableArray *session = [NSMutableArray arrayWithArray:someArray];
1. is this approach correct?
Yes.
2. I didn't find any way to do this (sessionData = requestResult)
As many have suggested you can use mutableCopy to assign requestResult to sessionData
OR you can use arrayWithArray as one answer suggests.
3. do I have to initialize in some way the NSMutableArray before using ?
Yes. If you are changing any variable it must have memory allocated.
In your example something like this:
NSArray *requestData = [[NSArray alloc] initWithObjects:#"3", #"4", #"5", nil];
_sessionData = [[NSMutableArray alloc] initWithArray:requestData];
[requestData release];
NSLog(#"%#", [sessionData objectAtIndex:0]); // 2012-03-30 15:53:39.446 <app name>[597:f803] 3
NSLog(#"count: %d", [sessionData count]); //2012-03-30 15:53:39.449 <app name>[597:f803] count: 3

Memory management while copying objects

I know that my question has already been discussed on StackOverflow but i found the answer not complete for my needs. So the question is:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
secondArray = [firstArray mutableCopy];
what is retain count for the secondArray now? 2 or 1? Should i release it twice or just once?
Does copy or mutableCopy increases retain count of the COPYING (secondArray in this event) object?
You should never care about the absolute retain count. Only that you're "balanced", that means for every alloc, new*, copy, mutableCopy and retain you need a corresponding release or autorelease (when not using ARC, that is).
If you apply this rule to each line you can see that your second line has an alloc, but there's no release. In fact, it's absolutely useless to allocate an instance here since you're not interested in it anyway. So it should simply read:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [firstArray mutableCopy];
// There is no third line.
But let's discuss your original code and see what happened:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
// secondArray points to a new instance of type NSMutableArray
secondArray = [firstArray mutableCopy];
// You have copied another array (created a new NSMutableArray
// instance) and have overwritten the pointer to the old array.
// This means that the instance allocated in line 2 is still there
// (was not released) but you don't have a pointer to it any more.
// The array from line 2 has been leaked.
In Objective-C, we often speak of ownership: there are very few methods that make you the "owner" of an object. These are:
alloc
new*, as in newFoo
copy and mutableCopy
retain
If you call these, you get an object for which you are responsible. And that means you need to call a corresponding number of release and/or autorelease on these objects. For example, you're fine if you do [[obj retain] retain]; and then [[obj autorelease] release];
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
secondArray = [firstArray mutableCopy];
What is happening is that you've created a memory leak. You just lost the reference assigned to secondArray when you overwrote it with the mutableCopy of firstArray with this line.
secondArray = [firstArray mutableCopy];
If you then release secondArray twice, the program will crash because you're then overreleasing the mutable array assigned by
secondArray = [firstArray mutableCopy];
What you need to do is to make sure you're not overwriting retained references by mistake, and balance retains with releases.

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

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

Fill NSMutableArray from another NsMutableArray

I have two classes indexViewController and flashCardQuestionViewController.
In the indexViewController i have table filled with an array.
Now i am getting some data from the database:
-(void)getMultipleChoiceAnswer
{
if(optionid!=nil)
[optionid removeAllObjects];
else
optionid = [[NSMutableArray alloc] init];
if(optionText!=nil)
[optionText removeAllObjects];
else
optionText = [[NSMutableArray alloc] init];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:"select optionID,OptionText from flashCardMultipleAnswer where questionId=1"];
while(sqlite3_step(dataRows) == SQLITE_ROW)
{
[optionid addObject:[NSNumber numberWithInt:sqlite3_column_int(dataRows,0)]];
[optionText addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)]];
}
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
and I am calling this method in the viewDidLoad method of the indexViewController.
Now I have another NSMutableArray in the flashCardQuestionViewController named listNoOfOptionsInQuestion.
I want to fill listNoOfOptionsInQuestion with objects from optionText array in indexViewController.
How can I do this?
There are a number of ways to copy arrays: you can either use -[NSArray copy] to get an immutable copy, or -[NSArray mutableCopy] for a mutable copy. Don't forget that copy adds a reference so you'll need a release or autorelease somewhere (if you're not using GC that is).
Alternatively, you can use -[NSMutableArray addObjectsFromArray:].
Given your example, it looks like you want to do something like this at the end:
[flashCardQuestionViewController setListNoOfOptionsInQuestion:optionText];
And then in FlashCardQuestionViewController, you want something like:
- (void)setListNoOfOptionsInQuestion:(NSArray *)options
{
if (options != listNoOfOptionsInQuestion) {
[listNoOfOptionsInQuestion release];
listNoOfOptionsInQuestion = [options mutableCopy];
}
}
Rahul,
Do you really need to have a completely different copy of the MutableArray in each object. Would it be possible to have both objects point to the same array? For instance:
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = mArray;
Or do you need to make changes to the two arrays in different ways? The try this (as suggested by Chris above) :
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = [mArray mutableCopy];
again, if this isn't what you need then you'll have to give us a more precise question or problem that we can identify.