Objective-c NSMutableArray release behaviour - objective-c

Does the release, recursively releases all inner objects? or must it be done manualy?
Can I do just this?
NSMutableArray *list = [[NSArray alloc] init];
// ...
// fill list with elements
//...
[list release];
Or must I release all inner objects one by one before releasing the NSMutableArray? // Suposing there isn't any other reference to the contained objects, except on the list itself.

Yes it does. It retains them when added, and releases them when dealloc'd. This is actually one of the most common questions I see here.

If you are owning the object then you will have to release it.
NSMutableArray *list = [[NSArray alloc] init];
NSString *str = [[NSString alloc] init] // you are the owner of this object
[list addObject:str];
[str release]; // release the object after using it
[list release];
If you are not the owner of the object then you should not release.
NSMutableArray *list = [[NSArray alloc] init];
NSString *str = [NSString string]; // you are not owning this object
[list addObject:str]; // str retain count is incremented
[list release]; // str retain count is decremented.
This is the concept which even array also uses. When you add any object to the array, array will retain it. In the sense it becomes the owner of that object and It will release that object when you release the array.

Related

NSArray copy for undo /redo purposes

If I have an nsarray full of custom objects and I make a second array using:
NSArray *temp = [NSArray arrayWithArray:original];
then work with some properties of the objects inside the original array, then decide to roll back, I am then using the reverse:
original = [NSArray arrayWithArray:temp];
I am finding the objects I changed in the array also effected my temp array. I also tried implementing copyWithZone on my custom class, and using copyItems and it did not help. What else should I try?
To be clear, in order to use copyWithZone, I changed my array creation command to:
NSArray *temp = [[NSArray alloc] initWithArray:original copyItems:YES];
My copyWithZone:
-(id)copyWithZone:(NSZone *)zone{
CustomObject *ret = [[CustomObject allocWithZone: zone] init];
//copy properties
return ret;
}

Objective C: Releasing a list, will it cause potential leaks?

Tried finding the answer online, but couldn't. So i'm wondering if anyone else knows and why?
Say I have an NSDictionary, or NSArray, that stores objects inside of them. If I release the NSDictionary, is there a potential leak because I didn't release the objects inside of the NSDictionary list?
For example:
NSDictionary *dict = [NSDictionary alloc] init];
// Create a bunch of objects, NSStrings, etc.
// Store it into dict.
[dict release];
Will that also release everything inside of the dict? (objects, nsstrings, etc).
Thanks in advance people!
All items in an NSDictionary or NSArray are automatically retained when they're added and released when removed, or when the list is destroyed.
For example:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
MyObject *obj = [[MyObject alloc] init];
[dict setObject:obj forKey:#"foo"]; // the dictionary retains "obj"
[obj release]; // this matches the "alloc/init"
// but "obj" still is retained by the dictionary
[dict release]; // now "obj" gets released
When you do a release on a NSDictionary or NSArray, as long as the retain count of the objects inside the array is 1 (meaning as long as you released the objects after you inserted them inside the data structure), then once you release the dictionary or array, those objects will be released as well.

I cant find this memory leak. I thought I was releasing everything properly

I cannot find this memory leak. I thought I had been releasing things properly. Here is the block of code in question.
- (void) createProvince:(NSString *) provinceName {
// if province does not exist create it
if ([self hasProvince: provinceName] == NO) {
// get the province object
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat:#"Name == %#", provinceName];
NSMutableArray *provArray = [[NSMutableArray alloc] init];
[provArray setArray: [CoreDataHelper searchObjectsInContext:#"Province" :predicate :#"Name" :YES :[self managedObjectContext]]];
NSIndexPath *indexPath;
indexPath = [NSIndexPath indexPathForRow:0 inSection: 0];
[[self provinces] addObject: [provArray objectAtIndex: [indexPath row]]];
[provArray release];
// create a cities array to hold its selected cities
NSMutableArray *array = [[NSMutableArray alloc] init];
[[self cities] addObject: array];
[array release];
}
}
The leaks are here:
[[self provinces] addObject: [provArray objectAtIndex: [indexPath row]]];
NSMutableArray *array = [[NSMutableArray alloc] init];
[[self cities] addObject: array];
I am creating the local variables, assigning them to my instance variables through the proper setters, and then releasing the local variables. I am not sure what is going on.
Do you have a dealloc method that is properly releasing everything?
Note that leaks is showing you where something was allocated. It doesn't show you where it was actually leaked; what retain wasn't explicitly balanced.
Let's look at this:
NSMutableArray *array = [[NSMutableArray alloc] init];
[[self cities] addObject: array];
[array release];
When you alloc an object, its retain count is set to 1:
NSMutableArray *array = [[NSMutableArray alloc] init]; # retain count of array is 1
When you add an object to an NSMutableArray, that object's retain count is incremented:
[[self cities] addObject: array]; # retain count of array is 2
When you release the array, its retain count is decremented:
[array release]; # retain count is now 1
Once your method ends, you still have that array owned by the mutable array [self cities].
Because [self cities] doesn't appear to get released or emptied, this is where you get a leak.
You need to empty or release the mutable array at some point, releasing the objects contained within. If cities is a class property, perhaps release it when the class is released.
EDIT
Fixed init-alloc mistake.

Releasing an Array Object

According to Instruments analysis, I have a memory leak here - and - I'm unsure as to how to properly release the scoresArray object that I have created.
This code does work properly, apart from the leakage. I release the highScoresArray object later in the code - but attempts to release the scoresArray kill the app. I thought that when I released highScoresArray, that I would release scoresArray, since they both point to the same location in memory. If anyone can point out where my thinking is flawed, that would be great.
- (void) readScoresFile {
// Read the Scores File, if it exists
NSString *filePath = [self scoresFilePath];
// Only load the file if it exists at the path
if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) {
scoresFileExistsFlag = YES;
NSLog(#"SCORES FILE EXISTS - THEREFORE LOAD IT");
NSMutableArray *scoresArray = [[NSMutableArray alloc] initWithContentsOfFile: filePath];
highScoresArray = scoresArray;
} else {
scoresFileExistsFlag = NO;
NSMutableArray *scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
// No Scores File exists - we need to create and save an empty one.
int counter = 1;
while (counter <= 5) {
// Set up a date object and format same for inclusion in the Scores file
NSDate *now = [[NSDate alloc] init];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy.MM.dd"];
NSString *theDateNow = [dateFormat stringFromDate:now];
// Add the score data (Score and User and date) to the runScoreDataDictionary
runScoreDataDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], #"score",
[NSNumber numberWithInt:0], #"landings",
currentUser, #"user",
theDateNow, #"date", nil];
//NSLog(#"Dictionary contains: %#", runScoreDataDictionary);
// Add the dictionary to the highScoreArray
[highScoresArray addObject:runScoreDataDictionary];
//NSLog(#"OBJECTS in ARRAY: %i", [highScoresArray count]);
[self writeScoresFile]; // Write the empty scores file to disk
[now release];
[dateFormat release];
++counter;
//[scoresArray release]; // TESTING TO SEE IF THIS KILLS - YES KILLS
}
}
}
I'm guessing highScoresArray is an instance variable (since it's not declared anywhere in the method you listed). This means that upon creation of scoresArray (which is the same object as highScoresArray) it has a retain count of 1. You don't retain it, so releaseing it will decrement its retain count to 0 and it will be cleaned up -- not a good thing for an instance variable.
I'm also not sure why you do this:
NSMutableArray *scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
You don't seem to need to use scoresArray anywhere else, so you could just do this:
[highScoresArray release]; // Release the old object
highScoresArray = [[NSMutableArray alloc] init];
I release the highScoresArray object later in the code - but attempts to release the scoresArray kill the app. I thought that when I released highScoresArray, that I would release scoresArray, since they both point to the same location in memory
As long as you haven't changed the highScoresArray pointer to point to another object, releasing it will be the same as releasing scoresArray.
NSMutableArray* highScoresArray;
NSMutableArray* scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
[highScoresArray release]; // same as `[scoresArray release];`
But if you change either of them afterwards to point to another object, releasing them won't be equivalent:
NSMutableArray* highScoresArray;
NSMutableArray* scoresArray = [[NSMutableArray alloc] init];
highScoresArray = scoresArray;
// ... Now make `highScoresArray` point to another object ...
highScoresArray = [[NSMutableArray alloc] init];
// Now you should release both as they point to different objects.
[highScoresArray release];
[scoresArray release];
Of course, simply calling addObject doesn't alter the pointer. It changes the object being pointed to. Only reassigning the pointer to another object matters here.

Initializing an instance variable

With an instance variable myArray:
#interface AppController : NSObject
{
NSArray *myArray;
}
Sometimes I see myArray initialized like this:
- (void)init
{
[super init];
self.myArray = [[NSArray alloc] init];
return self;
}
and sometimes I see it with a more complicated method:
- (void)init
{
[super init];
NSArray *myTempArray = [[NSArray alloc] init];
self.myArray = myTempArray
[myTempArray release];
return self;
}
I know that there's no difference in the end result, but why do people bother to do the longer version?
My feeling is that the longer version is better if the instance variable is set up with a #property and #synthesize (possibly because the variable has already been alloced). Is this part of the reason?
Thanks.
If myArray is a property and it's set to retain or copy (as it should be for a property like this), then you'll end up double-retaining the variable when you do this:
self.myArray = [[NSArray alloc] init];
The alloc call sets the reference count to 1, and the property assignment will retain or copy it. (For an immutable object, a copy is most often just a call to retain; there's no need to copy an object that can't change its value) So after the assignment, the object has retain count 2, even though you're only holding one reference to it. This will leak memory.
I would expect to see either a direct assignment to the instance variable
myArray = [[NSArray alloc] init];
Or proper handling of the retain count:
NSArray *newArray = [[NSArray alloc] init];
self.myArray = newArray;
[newArray release];
Or the use of autoreleased objects:
self.myArray = [[[NSArray alloc] init] autorelease]; // Will be released once later
self.myArray = [NSArray array]; // Convenience constructors return autoreleased objects
This is an idiom used in mutators (sometimes called "setters"), but I think you typed it slightly wrong. Usually it looks like this:
-(void)setMyName:(NSString *)newName
{
[newName retain];
[myName release];
myName = newName;
}
The new name is retained, since this instance will need to keep it around; the old name is released; and finally the instance variable is assigned to point to the new name.
I have a feeling you mean this:
NSArray* a = [NSArray arrayWithObjects:#"foo", #"bar", nil];
and this
NSArray* a = [[NSArray alloc] initWithObjects:#"foo", #"bar", nil];
//...
[a release];
With the first style, the static method performs an alloc/init/autorelease on it for you so you don't have to. With the second style, you have more control over when the memory is released instead of automatically releasing when you exit the current block.
That code will crash your application. The second version only copies the pointer then releases the instance. You need to call [object retain] before releasing the reference.