this is so basic, but quite not sure how to release array object in object-c.
I know that adding an object to array retains the object.
and removing it from an array releases the object.
When I have an array(mutable or not),
does 'releasing the array itself' also removes objects inside the array(therefore sending release to each object)?
Or should I remove the object first and release the array to reverse the operation (which is retaining array and each object in it).
Thank you
Calling release on NSArray OR NSMutableArray instances will also release it's objects,
Related
I have an array of dictionaries, which works ok. I am trying to track down a memory access issue and want to know:
If I create autoreleased strings for the dictionary, for example one string is from:
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
for (NSString *file in dirContents)
When I do
NSDictionary *dictItem = [NSDictionary dictionaryWithObjectsAndKeys:file, #"file"...
Does the resulting dictItem retain the individual strings? I have been assuming so ;-)
And then I add that dictItem to an array. I assume the addObject call on NSMutableArray will retain the dictItem for me...
What about when it comes time to free up the memory?
If I simply call removeAllObjects on the array (which crashes now), will it release all of the strings inside the dictionaries as well as the dictionaries themselves?
Hope this is somewhat understandable ;-)
thanks!
Here's a quick whistle-stop tour of memory management in Cocoa collections...
NSDictionary does not retain keys. Instead it creates a copy of the key. In other words adding an object as a key in a dictionary will not increment its retain count.
NSDictionary will retain the values for the keys that you pass it.
When you release (and eventually dealloc) the NSDictionary it is responsible for calling release on the objects it has stored (not you!)
NSArray will retain any object you add to it
NSArray will release all objects it holds a reference to when it is deallocated
Knowing the above...
dicItem will not retain your key #"file" - it will copy it
dicItem will retain your object called file
when dicItem is deallocated it will call release on its copy of #"file" and file
when you add a dicItem to your array it will be retained
when you release your array and it is deallocated all the dicItems it holds will be sent a release message
releasing your array should subsequently trigger a dealloc of your dicItem objects - provided they are referenced elsewhere (i.e. leaked)
you do not need to send your NSArray object a removeAllObjects message - the array itself will clean up after itself.
I think you are making this a little more complicated than it has to be. The only memory management you need to really worry about is within your own classes. When adding to an NSArray it does in fact retain the object, and then when you call removeAllObjects or the array is destroyed, it will release all of it's contained objects. However, this is where the concept of "owning" an object is important. When any object gets a reference to an object, it can call "retain" on it to indicate that it owns the object (this also applies to allocating a new object or using the "new" operator). That means, the object knows for sure the object will be around until it is done with it. To indicate that it is done with the object, it should always release or autorelease the object and set the reference to nil.
So bottom line, if you do not call retain, alloc, or new on an object, you should never call release. If you do call one of those functions, you should ALWAYS call release or autorelease on it. For any other object that you may send a reference to, you can assume that it will take ownership of it if it wants and if it does, it will always release it when it is done.
In my iPhone development book, I'm seeing some strange coding examples in regard to what an array does when objects are added to the array and when the whole array is released. One code example has the following properties on an instance array:
#property (nonatomic, retain) NSMutableArray* myArray;
The author adds an object to the array and, immediately after, releases his pointer to the object. Won't the array cell now point to garbage data? Unless, behind the scenes, the array cell retains the object when added.
SomeObject* someObject = [[SomeObject alloc] init];
[self.myArray addObject:someObject];
[someObject release];
The author also releases the the pointer to the array without first going through each array cell and releasing the individual objects. This is a memory leak unless, behind the scenes, each cell is sent a release message;.
- (void)viewDidUnload {
self.myArray = nil;
[super viewDidUnload];
}
Unless, behind the scenes, the array cell retains the object when added.
Yes, this happens.
... unless, behind the scenes, each cell is sent a release message.
This also happens.
You have answered your own question.
Here is a quote from Collections Programming Topics:
And when you add an object to an
NSMutableArray object, the object
isn’t copied, (unless you pass YES as
the argument to
initWithArray:copyItems:). Rather, an
object is added directly to an array.
In a managed memory environment, an
object receives a retain message when
it’s added; in a garbage collected
environment, it is strongly
referenced. When an array is
deallocated in a managed memory
environment, each element is sent a
release message.
Unlike in C or C++ where you constantly worry about whether to delete an object or not for the fear of it is still being used somewhere else, Objective-C (or rather it's actually Cocoa SDK) uses the mechanism of reference counting or ownership.
You might already know how it works but you need to also know that in Cocoa, if an object A needs to use another object B it should own (i.e. retain) it. That object A should not rely on some other object C already retained B, because it cannot know when C releases it. So in your case, since NSArray needs to use all objects added to it latter during its lifetime, it needs to retain all the objects. And because of that, when the array is de-alloc-ed, it needs to release them.
This concept of "you need to retain what you want to use latter" is very important when you are dealing of lots of objects.
There are several places in apple development guides that explain that is a good practice to take the ownership of an object (send a retain message) if you plan to use it later. You should do it so that the object is not destroyed while you still might need to access it.
Considering that, you were right assuming that the NSArray retains the object when it is added to the collection, as it still might try to access it afterwards.
You can check the Memory Management Programming Guide
When you add an object to a collection such as an array, dictionary, or set, the collection takes ownership of it.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW3
or the Collections Programming Topics for more details
... In a managed memory environment, an object receives a retain message when it’s added.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Arrays.html#//apple_ref/doc/uid/20000132-SW1
You're right on the first point. When an object is added to an array, the array retains the object. Thus, for an object that has been previously retained, it is necessary to release it after adding it to the array or you can end up with a memory leak.
Likewise, when an object is removed
from an array, the array releases the
object. So, if you want to keep it,
you'll need to retain it.
When an array is released, as you
surmised, the array will release all
the objects it contains. Thus,
releasing each object individually is
not necessary and, in fact, would
raise an exception.
Finally, regarding the line of code
in -viewDidUnload that you quoted:
self.myArray = nil;
This works properly with regard to memory management as long as the myArray property was synthesized as follows:
#synthesize myArray;
Synthesizing creates a setter that effectively does the following:
- (void)setMyArray(NSMutableArray *)anArray
{
if (![myArray isEqual:anArray]) {
[myArray release];
myArray = anArray;
[myArray retain];
}
}
So, when called, the above setter will first release the old array (as long as it's not the same object as the new array.) Then, it will retain the new array, which in this case is nil. Note that retaining nil will just do nothing, and won't trigger an error.
Of course, if you don't synthesize the myArray property, or if you override the setter, you will have memory problems unless you also release the old value & retain the new in your setter.
If I have a grandfather object that contains an array of parent objects that contains an array of children objects. Assuming I have released the objects after adding them to the arrays, how do I go about releasing all the objects? Can I just call removeallobjects on the grandfather object? When I do this I get a leak :(
Thanks
Simply call release on the "grandfather" object - it'll then release the "parent" objects, who will release the "children" objects, etc. all the way down. (Or "up" depending on how you look at it.)
In essence when you release an NS(Mutable)Array, it'll release the objects it has pointers to - if those objects happen to be NS(Mutable)Arrays, they'll therefore release the objects they have pointers to...
I was wondering when you remove an object using removeObject in an array if that removed object is handled properly. Would the object being removed be released?
The NSMutableArray will release it. If that's the last retain on it, it will be deallocated. From the documentation:
Like NSArray, instances of NSMutableArray maintain strong references to their contents. If you do not use garbage collection [Jed: the iPhone does not], when you add an object to an array, the object receives a retain message. When an object is removed from a mutable array, it receives a release message. If there are no further references to the object, this means that the object is deallocated. If your program keeps a reference to such an object, the reference will become invalid unless you send the object a retain message before it’s removed from the array.
See the NSMutableArray documentation. Their example, in fact, refers to removeObjectAtIndex::
id anObject = [[anArray objectAtIndex:0] retain];
[anArray removeObjectAtIndex:0];
[anObject someMessage];
Yes. Collections retain values they collect when the values are added to the collection, which means that the values are released when they're removed from the collection.
Yes, when the object is removed from the NSMutableArray, it is released. If its retain count is 0, it will be deallocated (or garbage collected, if you were instead running on OS X with GC enabled).
As everybody has said, the object of a NSMutableArray is released after it is removed from the array.
If you don´t want to release the object, retain it just before you call remove object method. In this case, you are responsible for it to release it later:
MyClass *objectToBeRemoved=[myArray objectAtIndex:indexPath.row];
[objectToBeRemoved retain];
[myArray removeObject:objectToBeRemoved];
If I am adding an item to a dictionary like this:
[connectionHandlers setObject:projectsHandler forKey:[NSNumber numberWithInt:PROJECTS_CONNECTION_ID]];
Should I be retaining the NSNumber used for the key.
Also, when an object is added to an NSArray or NSDictionary, is it retained by the Array or Dictionary when added and released when removed?
I'm trying to get my head around Memory Management on the iPhone, and it's a doozy
You do not need to retain it. Anytime you add keys or objects to dictionaries or arrays, the array or dictionary will retain it. If you retain it as well, it will be unnecessary.