Releasing or Autoreleasing objects - objective-c

I have a problem understanding this code:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
// ------- Sets the subnet mask when the user selects the number of bits
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];//TURN A STRING INTO A NUMBER
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];//CONTAINS THE SELECTED NUMBER OF BITS
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//RELEASE
[stringToNumber release];
[selectedAmountOfBits release];
}
I kept getting errors because of the fact that I released selectedAmountOfBits.
I initialized the object using alloc and init.
Why don't I need to release it?

The problem is that you are assigning an object to selectedAmountOfBits twice.
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];
allocates a new NSNumber object that you own and assigns it to selectedAmountOfBits.
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
assigns a new autoreleased object to selectedAmountOfBits. This means that when you do [selectedAmountOfBits release], you are actually trying to release an object that you do not own. You are also leaking the original NSNumber that you created since you've lost any reference to it.
The solution is to remove the alloc/init line, keep the autoreleased NSNumber, and get rid of the line where you release it. The final code should look like this:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
// ------- Sets the subnet mask when the user selects the number of bits
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];//TURN A STRING INTO A NUMBER
NSNumber *selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//RELEASE
[stringToNumber release];
}

There are some issues in the original code, I added //!i comments below:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];
//!i: The [[NSNumber alloc] init] is unnecessary. You are creating a pointer to a dummy number
// that is immediately overwritten in the next line
NSNumber *selectedAmountOfBits = [[NSNumber alloc] init];
//!i: At this point, you overwrite the pointer stored in selectedAmountOfBits to point to a new
// NSNumber, returned by numberFromString:, and in the autorelease pool
selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
//!i: you are now leaking the number allocated via [[NSNumber alloc] init], as you no longer have
// a variable tracking the pointer to it
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
[stringToNumber release];
//!i: You are calling -release on the number that is in the autorelease pool, not on the
// original number you allocated via [[NSNumber alloc] init]
[selectedAmountOfBits release];
}
You can fix this as follows:
- (void)subnetMaskByNumberOfSubnetBits:(id)sender{
NSNumberFormatter *stringToNumber = [[NSNumberFormatter alloc] init];
NSNumber *selectedAmountOfBits = [stringToNumber numberFromString:[sender objectValueOfSelectedItem]];
[self changeSubnetMaskUsingNumberOfMaskBits:selectedAmountOfBits];
//!i: You still need the -release here, as stringToNumber points to the
// NSNumberFormatter that you created using alloc/init
[stringToNumber release];
}

Related

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.

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.

Releasing an NSString that I am done with causes a crash

Note the commented-out [printvolfirst release]; line below. If I un-comment it, the program crashes. I can't figure out why. The printvolfirst variable is not used anywhere else except in the lines of code you see here. After it is assigned to printvol I'm done with it. So why not release it?
vol = vol / 1000000;
NSNumberFormatter * format = [[NSNumberFormatter alloc] init] ;
[format setPositiveFormat:#"#.#"];
NSString * printvolfirst = [[NSString alloc]init];
printvolfirst = [format stringFromNumber:[NSNumber numberWithFloat:vol]];
NSString * printvol = [[NSString alloc] initWithFormat: #"%#M", printvolfirst];
self.Pop.vol.text = printvol;
[printvol release];
//[printvolfirst release];
[format release];
stringFromNumber: autoreleases the returned object. If you release it again, it's released after it has been deallocated.
In fact, you don't even need this code:
NSString*printvolfirst=[[NSString alloc]init];
You can turn on 'Run Static Analyser' in the build settings to get warned about such things.
You are deallocating an autoreleased string. Although you are doing NSString*printvolfirst=[[NSString alloc]init];, you are losing the reference to that object when you do printvolfirst=[format stringFromNumber:[NSNumber numberWithFloat:vol]]; where you assign an autoreleased object to printvolfirst. In the process, you have also created a memory leak. You don't have to release it.

Over-releasing an NSDecimalNumber

My app is telling me that I'm over-releasing the NSDecimalNumber tempDouble below:
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setLocale:[NSLocale currentLocale]];
[currencyFormatter setGeneratesDecimalNumbers:TRUE];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Here is the key: use the maximum fractional digits of the currency as the scale
int currencyScale = [currencyFormatter maximumFractionDigits];
NSDecimalNumber* tempDouble = [[NSDecimalNumber alloc] initWithString:self.tempStore];
NSDecimalNumber* numTen = [[NSDecimalNumber alloc] initWithInt:10];
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
[numTen release];
[textField setText:[currencyFormatter stringFromNumber:tempDouble]];
[currencyFormatter release];
[tempDouble release];
I'm thinking that the problem line is this one:
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
But I'm not sure why. Should I be adding an 'assign, copy or retain' attribute after the assignment? When I get rid of the 'release' statement below, the code works fine.
Thanks,
G
The problem is indeed in this line:
tempDouble = [tempDouble decimalNumberByDividingBy:[numTen decimalNumberByRaisingToPower:currencyScale]];
What you're doing here is replacing the previous value in tempDouble (which has a retain count of 1) with a new value (which is autoreleased).
Here's the correct way to do it:
NSDecimalNumber* tempDouble = [[[NSDecimalNumber alloc] initWithString:self.tempStore] autorelease];
And then delete the [tempDouble release] from the end of the method.

NSNumbers in archived array dissappear

Okay I've made an array of NSNumber objects that I've stored into an NSMutableArray. When the game loads it's unarchived and retained but all the NSNumber objects are gone. Here's my code:
times = [[NSMutableArray alloc] initWithObjects:[[NSNumber alloc] initWithFloat:time],nil];
[NSKeyedArchiver archiveRootObject:times toFile:#"times"];
...
NSMutableArray *newTimes = [[NSKeyedUnarchiver unarchiveObjectWithFile:#"times"] retain];
times = [[NSMutableArray alloc] initWithArray:newTimes];
[newTimes release];
Please help.