Memory management while copying objects - objective-c

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.

Related

What is the difference between these two lines of Objective-C code?

NSArray *emptyArray = [NSArray array];
NSArray *emptyArray = [[NSArray alloc] init];
Both of them seem to create empty array. So what's the difference?
They do a similar thing, but not quite exactly the same thing.
[NSArray array] creates an autoreleased array
[[NSArray alloc] init] creates an array with a retain count of 1 making emptyArray its owner.
With ARC, you won't notice much of a difference, but with manual memory management you will. If you're using ARC you can probably safely use either, but I'd recommend you stick to one format (I personally prefer to use alloc/init).
Here is an excerpt of NSArray.m from GNUstep which follows closely to Apple's Cocoa (formerly NeXT's OpenStep):
/**
* Returns an empty autoreleased array.
*/
+ (id) array
{
id o;
o = [self allocWithZone: NSDefaultMallocZone()];
o = [o initWithObjects: (id*)0 count: 0];
return AUTORELEASE(o);
}
The difference is how the memory is managed:
NSArray *emptyArray = [NSArray array];
In this case emptyArray is pointing to an autoreleased array object, meaning that you don't own the object. But also this means that, if you're NOT using ARC, you can't use this object after the current run loop, unless you increment it's retain count, because it could be destroyed.
NSArray *emptyArray = [[NSArray alloc] init];
In this case you get an array with a retain count of +1. This means that you're responsible for deallocating the memory when you're done using it.
This difference is specially important if you're not using ARC.
NSArray *emptyArray = [NSArray array];
is similar to
NSArray *emptyArray = [[[NSArray alloc] init] autorelease];
This means, [NSArray array] returns auto released object. So you do not need to release object created using [NSArray array].
But you are responsible to release object created using [[NSArray alloc] init].
Basically new instances returned from class methods return autoreleased instances which indeed means IOS holds ownership of it and thus no need to worry on releasing it.
NSArray *arr=[NSArray array]; is same as NSArray *emptyArray=[[[NSArray alloc] init] autorelease];
When using [[NSArray alloc] init] if you are using MRC then you are responsible to release while it is taken care automatically for you in ARC.
Hence irrespective of ARC or MRC it is always better to use [NSArray array].

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.

NSMutableArray initialization methods

Is there a difference between [NSMutableArray array] and [[NSMutableArray alloc] init] ?
[[NSMutableArray alloc] init] returns a mutable array that you own (and therefore have to explicitly relinquish ownership using release when you no longer need it) and [NSMutableArray array] returns a mutable array that you don't own.
According to the Memory Management Rules, any method with the word alloc, new, or create in the name, implies that you own the object returned from that method.

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

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.