Objective C - Leak when setting an array as an object of a dictionary - objective-c

I wrote a class, which acts as a filter. I pass three objects:
An NSArray, which holds objects to filter (these objects have a timestamp property)
An NSMutableArray (which will hold the section names for a tableView, the periods based on timestamps). I need this array, because I have to sort the periods.
An NSMutableDictionary, in which the keys will be the section names, the values are NSMutableArrays, which hold the items for a given period.
In the class from which I pass these objects, there is a tableView, in which I display the items.
This class has it own NSMutableArray and NSMutableDictionary, I not initialize them, only retain the corresponding return values of the filter class. In the delloc method I release them. There is a method in the filter class:
+ (void)insertItem:(id)item forPeriod:(NSString *)period toContainer:(NSMutableDictionary *)container {
if ( ![[container allKeys] containsObject:period] ) {
// the period isn't stored, create and store it
NSMutableArray *periodArray = [[NSMutableArray alloc] init];
[container setObject:periodArray forKey:period];
[periodArray release];
periodArray = nil;
}
// store the item
NSMutableArray *arrayForPeriod = [container objectForKey:period];
[arrayForPeriod addObject:item];
arrayForPeriod = nil;
}
The instruments shows me leak when I set the newly allocated array as an object of the dictionary. At this point this is definitely true, because the dictionary retains again the array, so after the release, it retain count remains 1. But I think in the caller class when I release the dictionary, the array will be released too. Am I wrong?

Yes it is considered as a leak because your var is a local variable. Then you still have an object in memory but no reference to it. Remember the init makes a retain + the retain made by the dictionary = 2 retains. Just create your array using
NSMutableArray *periodArray = [[[NSMutableArray alloc] init]
autorelease]
Is it clear ?

You could switch to ARC. Alternatively, check what the static analyser thinks of your code. It is pretty good at finding memory leaks, better than most humans.
Once you have a few hundred objects in your dictionary, you waste an awful lot of time and memory. A dictionary doesn't have an array of all keys stashed away somewhere, it has to create it every time you call your method. That's copying a few hundred pointers (cheap) and retaining them (expensive). containsObject for an array compares the object with every object in the array calling isEqual: That's expensive. It's an NSString compare each time. The array is autoreleased, and when it finally goes away, all the keys in it get released. Again expensive.
NSDictionary uses a hash table, so [objectForKey ] will immediately go to the right object. One operation instead of possibly hundreds.

Related

Objective-C - Initializing an already initialized object?

What happens if I call [alloc] init] on an object which already was initialized and alloc'ed?
In my particular case I have an NSMutableArray which I initialize in superclass Parent using NSMutableArray* someArray = [NSMutableArray alloc] init];
In subclass Child I need to insert an object in someArray but at a specific index, for example 3.
So if the array has no items, or if it has less items than the index I'm trying to insert at (array has 4 items, and I want to insert at index 10) it will crash.
What would happen if I initialized someArray again in Child class? Would the pointer stored in someArray be replaced with the new one I'm initializing and the "old" one would just leak?
EDIT:
Sorry, my terminology was a bit off. I don't mean doing [someObject alloc], but doing someObject = [SomeClass alloc] init]; where someObject had previoulsy been initialized with an instance of SomeClass
Just for clarity when you say "What happens if I call [alloc] init] on an object..." your terminology is wrong.
The following line:
NSMutableArray* someArray = [[NSMutableArray alloc] init];
Reads in English:
"Send the alloc message to the NSMutableArray class object, then send the init message to the object returned from the first message, then store the object returned from init into the pointer variable named someArray."
I say that to emphasize the fact that you're not "calling alloc/init" on an existing object, you're making a new object, and storing a reference to this new object over the reference you had to the previous object. Since you no longer have a reference to that previous object, you've lost the ability to properly release its memory, so yes, you'll leak it.
correct, it will leak. Use NSMutableArray insertObject:atIndex‎:
There are a couple of ways that come to mind to do what I think you want. A sort of clumsy one is to put as many [NSNull null] objects into the array as you need so that it's filled up to the spot where you need to add the new object. Then you would replace an existing NSNull if you were storing your own object.
Probably a better approach is to use a dictionary instead of an array and turn your index value into a key.

Set an object to nil after added them to a container?

I thought that NSArray/NSDictionary/NSSet and their mutable subclasses just added the pointer to the object, and not the object it self.
So if set my "simple" object to nil after I added it to the container, why isn't the reference nil also in the Array (container)?
Here is the code:
NSMutableArray *array = [[NSMutableArray alloc] init];
Simple *simple = [[Simple alloc] init];
[array addObject:simple];
//Array sends retain, lets release
[simple release], simple = nil;
NSLog(#"Simple = \"<Simple: %p>", simple);
NSLog(#"Array: %#", array);
[array release], array = nil;
Here is the output:
2011-02-16 20:00:03.149 Allocations[5433:207] Simple = <Simple: 0x0>
2011-02-16 20:00:03.150 Allocations[5433:207] Array: (
<Simple: 0x4d3d4e0>
)
NSArray adds a pointer to the object. In order to track changes to variable, the array would have to add a pointer to the variable itself (remember, you're just setting the variable to nil, not the object). There can be many variables all pointing to the same object, and reassigning them won't change any others.
Remember: Pointers aren't magic. They're just ordinary variables whose value is a memory address — in this case, the memory address of an object. Two pointers to the same object aren't "linked" any more than two ints with the value 5. Changing the pointer doesn't affect the object; in order to affect the object, you have to either send it a message that causes it to change (e.g. [object setValue:6]) or dereference the pointer to access the object's members directly (e.g. object->value = 6).
PS: Don't access an object's members directly. It's bad and fragile and very prone to bugs. I just mentioned it here to explain how pointers work.
Setting simple = nil just makes that pointer point to nothing. It doesn't delete the object that the array still has a pointer to. At the point of your NSLog statements, the retainCount of the Simple instance that simple pointed to would be one.
Create simple
simple => (Simple instance: retain count 1)
Add to array
simple => (Simple instance: retain count 2)
[array objectAtIndex:0] => (Simple instance: retain count 2)
Release simple
simple => (Simple instance: retain count 1)
[array objectAtIndex:0] => (Simple instance: retain count 1)
Set simple = nil
simple => nil
[array objectAtIndex:0] => (Simple instance: retain count 1)
Release array
(Simple instance: retain count 0, subsequently destroyed)
NSArray does contain only a pointer to the object that is added, but that's ok -- it's not pointing to the simple pointer itself, but rather to the Simple object that simple pointed to. Thus in your example, after you change what simple points to, the array is still pointing at the original Simple object.

Deallocating NSMutableArray of custom objects

I need help with deallocation of my NSMutableArray of custom objects. I need to retain the array and so I have added a property in .h and I release it in dealloc in .m file. When I add objects to the array, I do the following:
myarray = [[NSMutableArray alloc] init];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj1]];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj2]];
Now, I don't know how to release mycustomobject. If I do the following:
[myarray addObject:[[[mycustomObject alloc]initWithObject:obj1] autorelease]];
I run in to problems when I access the array later. Please advice.
I don't think you understand how memory management in Cocoa works. The array will retain the objects you add to it, and it will release them by itself when the array no longer needs them (such as when you release the array).
In other words, add the autoreleased object to the array, and don't worry about its retain count after that. If you want to remove it from the array simply remove it (using removeObjectAtIndex: or something similiar). If you think you want to release the object without removing it from the array then you are doing something wrong, since that may leave a dangling pointer in your array that will cause you to crash later.
You should really really go over the documentation again, particularly the section on Object Ownership and Disposal.
The proper way to do this is to let the array maintain ownership of the custom object:
NSMutableArray * array = [[NSMutabelArray alloc] init];
for (id obj in anArrayOfObjects) {
mycustomObject * customObj = [[mycustomObject alloc] initWithObject:obj];
[array addObject:customObj];
[customObj release];
}
If you're having difficulties accessing your array later, then you're doing something wrong with the memory management of the array.

Who is responsible for releasing objects in an array when copying?

In Objective-C, if array1 is copied onto array2 using mutableCopy, and suppose the code is done in main(), who is responsible for releasing the objects contained in the array? Is it main() or array2?
I think the previous answers have missed the point, or else the asker was pretty unclear. The actual question isn't talking about either array, but rather the array contents:
who is responsible for releasing the objects contained in the array? Is it main() or array2?
Both array1 and array2 are responsible for releasing the objects.
From the NSArray documentation:
"Arrays maintain strong references to their contents—in a managed memory environment, each object receives a retain message before its id is added to the array and a release message when it is removed from the array or when the array is deallocated."
To begin with, each of the objects are retained by the NSArray array1. When you create array2 via -mutableCopy, you get an NSMutableArray which points to the same objects, and retains each of them again. If you were to release array1 at this point, when its dealloc method were called it would release each of the objects it contains. However, array2 has retained them, so the objects won't be destroyed — only when their retain count reaches 0, which would happen if array2 were destroyed and nobody else has retained any of the objects (or when they are removed from array2).
Since collection classes (arrays, sets, dictionaries, etc.) handle retaining and releasing their contents, all you have to worry about is retaining or releasing the collection itself. Since you used -mutableCopy, remember that you have implicitly retained array2, so you should release it when you're done with it.
I reference this guide for Memory Management in Obj-C. He has a section on Arrays and Dictionaries, here's an excerpt:
Arrays, dictionaries etc. generally retain any objects added to them. (When dealing with 3rd party collection type objects, always check the documentation to see if they retain or not). This means that these collections will take ownership of the object, and you do not need to retain before adding.
The comments for the posting are also useful
The ownership responsibilities are not changed by storing objects in an array. Here's an example:
int main(int argc, char *argv[])
{
// ...
NSObject *obj1 = [[NSObject alloc] init]; // owned
NSObject *obj2 = [[NSObject alloc] init]; // owned
NSObject *obj3 = [[[NSObject alloc] init] autorelease]; // not owned
NSMutableArray *array1 = [NSMutableArray arrayWithObjects: obj1, obj2, obj3, nil]; // not owned
NSMutableArray *array2 = [array1 mutableCopy]; // owned
// ...
[array2 release];
[obj2 release];
[obj1 release];
// ...
}
This code directly allocates obj1 and obj2, so it owns them and must release them, but it autoreleases obj3, so it doesn't have to release that. In the same way, it doesn't own the result of arrayWithObjects:, so it doesn't release that, but it does own the result of mutableCopy, so it must release that. The objects being stored in an array is irrelevant—all you need to care about is ownership.
Both arrays keep strong references to their content, so obj1, obj2, and obj3 won't be deallocated as long as the arrays exist—but that's a detail of the NSArray contract, it doesn't affect how you manage the ownership of the objects or the arrays.
These are all details of Cocoa's memory management conventions, not arrays.
It wouldn't make sense for a mutable array to be tied to an immutable array. main() would be responsible for releasing array1.
In my experience however, releasing objects only causes applications to crash. ObjC is fairly good at automatically managing memory. My Cocoa apps don't seem to ever need more memory than they started with, even after running several hours.

Populating Object in Objective-C

I'm trying to populate a dictionary dynamically in a for loop like so:
pseudo-code
myObject = new Object
myDict = new Dictionary
for(int i; i < 10;i++)
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic = value:myObject key:i
end for
So my question is in Objective-C, can I just keep assigning new data to the myObject without releasing it every time in the loop? I'm still trying to figure out memory management in Objective-C.
That depends on what myObject actually is and how you have its properties defined. Assuming you have them defined to release such as here:
#property (nonatomic, retain) SomeClass *myProperty;
then yes, you can do that. The setters synthesized automatically for you by objective-c release and set to nil old values of properties before retaining and assigning new values.
However, although there is no problem with your memory management, there is still a problem with your code. Remember that myObject is a pointer, just like in C or C++. That means that if you add it to myDic and then modify the properties of the object later (such as on the next iteration of your for loop), those changes will be reflected when you pull the object out of the dictionary at some point in the future. What you want is something like this:
myObject = nil
myDict = new Dictionary
for(int i; i < 10;i++)
myObject = new Object
myObject.value1 = new data from somewhere
myObject.value2 = new data from somewhere
myDic setValue:myObject forKey:i
myObject release
end for
What this does is release myObject after putting it in the dictionary (all cocoa collection classes retain any object you put into them and release the objects when they are either removed or the collection itself is deallocated) so you don't have a leak, as well as allocate a new instance of Object at every iteration so you aren't modifying the same object over and over again.
If you haven't been reading Apple's Memory Management Guide for Cocoa, I'd highly recommend doing so. It's very informative.
Memory management in Objective-C is done through reference counting. When you allocate an object, it has a reference count of 1. The 'retain' method increases the reference count while 'release' decreases it. When the reference count reaches 0, the 'dealloc' method is called (you should never call 'dealloc' explicitely) and the object is freed.
You can also call 'autorelease' on an object, which will decrease the reference count "some time later". This allow you to make use of the object without worrying about releasing it.
For your question. When you add an object to a container, a 'retain' call is made on the inserted object. This means that you have to 'release' the inserted object:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
for( int i = 0; i < 10; ++i ) {
Object *myObject = [[Object alloc] init];
[myDict setObject:myObject forKey:[NSNumber numberWithInt:i]];
[myObject release];
}
You could also use:
Object *myObject = [[[Object alloc] init] autorelease];
and you wouldn't have to call 'release' after the insertion.
Your question is very open, it depends on how your Object and Dictionary is implemented.
First, you allocate object only once and release it in loop 10 times -> memory problem.
If we get past that. Assume that you allocate new object in every iteration of loop.
Take NSMutableDictionary and MyObject that extends NSObject as an examples of your Object and Dictionary.
When you call setObject:forKey on NSMutableDictionary instance, the object will receive retain call so dictionary keeps reference of it.
When you release it at the end of iteration the dictionary still keeps reference to it so it is ok.
Another thing to keep in mind if you use this in a big loop:
Object *myObject = [[[Object alloc] init] autorelease];
is the fact that autoreleased objects go to autorelease pool. Pool gets cleaned at the end of current event processing. If you create lots of objects it can take a lot of time to get through it at the end of event processing. In that case you might chose to create your own autorelease pool only for the loop - but I guess that's more advanced topic.
Definitely have a look at some Objective-c and memory management references from Apple.
What you're doing works with only one object and puts that one object into the dictionary ten times. It does not put ten separate objects into the dictionary.