Is "removeAllObjects" supposed to release? - iphone

I decided to start using removeAllObjects so that I could re-use some of my NSMutableArrays. However, I am finding that the call to removeAllObjects causes the array to get released and so it crashes when the viewController is displayed for the second time.
I've looked on the web and it seems that some people are saying that removeAllObjects just clears their array, while others are saying that it also releases the array. Which is true? It appears to release for me, but I find that weird and would expect the behaviour to be just to release the objects within the array. Nothing in the documentation warns of this, which I also find strange.
EDIT: With NSZombies turned on, all I get back is:
-[__NSArrayM removeAllObjects]: message sent to deallocated instance 0x625e4e0
Basically telling me that because the array was released due to the removeAllObjects, it can't call removeAllObjects second time round... Argh!
Can anyone help please?
Thanks!

removeAllObjects releases the objects in the array but has no effect on the array itself (except to make it empty).
Unless
one or more of the objects in the array had a retained reference to the array itself.

Okay so I was being an idiot!
I was doing:
[myArray removeAllObjects];
myArray = someOtherObject.someArray;
therefore resetting the pointer to some other object. I've now changed it to this:
[self.myArray removeAllObjects];
[self.myArray addObjectsFromArray:someOtherObject.someArray];
And it's now okay. I'm an idiot! Sorry all, thanks for your help!

removeAllObjects does not release the array itself, as you rightly expect. If it is being released the problem is somewhere else. You can use the NSZombie's instruments to check where it is being released.

If you are autoreleasing your MutableArrays, they might be released when they are empty as they are after a call to removeAllObjects. Use NSZombies to look where it is released exactly.

NSZombies can't tell you where the object is being released, so it shows you the first time you send a message to the deallocated object.
Work backward from that call to -removeAllObjects, and you'll find the bug.

[yourArray removeAllObjects];
it seems that the element of yourArray will release aotuomatically

Related

Memory Leaks in Objective-C / Arrays

I'm looking at someone's code and I know the general rule is if you have alloc/init, you need to release that memory. He uses a lot of NSMutableArrays, alloc/inits them, but does not release them. Can I simply send the autorelease message to the array that gets created if I do not see any other release/autorelease message getting sent to that array? I basically don't want to get his code to crash and stop working either :P.
With NSMutableArrays, when you send the message addObject and the object in that array increases its retain account, if that array gets released, but the object never gets sent a release or removeObject from the array, is that also a memory leak? Thanks.
You need to either -release or -autorelease anything you -retain, +alloc, -copy, +allocWithZone: or -copyWithZone:. (And, if you retain something twice you also need to release it twice.)
When an NSMutableArray (or NSArray, NSSet, or NSDictionary and mutable subclasses) object is dealloc'd (retain count reaches zero), it releases anything it contains. When you add an object to an NSMutableArray, the array retains the object (it does not copy it like some people claim).
I highly recommend the Memory Management Programming Guide to both you and the someone you referred to in the question.
I hope this answer helps you and someone. Good luck. :)
Also, enable the Clang Static Analyser in the build settings. This will tell you at compile time when a leak is going to happen (and much, much more). In fact, it's the first thing I always do when I start a new project. The analyzer never lied to me.
NSArray and NSMutableArray release all of their objects when they are destroyed, so if the array is managed properly with retain and release (or autorelease) the objects within will not be leaked. But if there are arrays that are never released, they will leak, along with everything inside them.
Without seeing the code, it's hard to advocate for just adding autoreleases everywhere, but for arrays that are used only in the context of a single function (and not assigned to ivars or static variables), the answer is yes, they should be autoreleased. Or more idiomatically, create them with methods like +arrayWithCapacity, which returns an object that has already been added to the autorelease pool.
It is general practice to release all the objects you initialize. But in your case, releasing the array should release all objects as well.
But it all depends on how you are using your objects !!!

I have to retain a NSMutableArray although it's a property

I'm new, but I read a lot about memory management, and really tried to find the answer myself.
It sounds so basic and yet I apparently don't get it.
I have a NSMutableArray as property in .h:
#property (nonatomic, retain) NSMutableArray *documentListArray;
I synthesize it and release it in (void) dealloc.
To populate the array, I have a method - (void)updateRecordList and in there:
self.documentListArray=[DocumentDatabase getDocumentListSortedByDate];
EDIT:next line:
[[self recordListTableView] reloadData];
where DocumentDatabase is a different class with the class methods getDocumentListSortedByDate and getDocumentList.
Here is what is happening in getDocumentListSortedByDate:
NSMutableArray *returnArray = [DocumentDatabase getDocumentList];
//sorting...
NSLog("array count:%i",[returnArray count]); //returns correct numbers first and second time
return returnArray;
and in getDocumentList
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:files.count];
//add file data to array...
return returnArray;
This works the first time I call updateRecordList, but after adding a file and calling updateRecordList a second time, it crashes with (using NSZombies):
* -[NSCFNumber dealloc]: message sent to deallocated instance 0x7504c90.
With a lot of logging I narrowed the problem down to the line above in updateRecordList and it works if I change it to:
self.documentListArray=[[DocumentDatabase getDocumentListSortedByDate] retain];
My conclusion is that the array down in getDocumentList has been autoreleased before it arrives. So my questions are:
1. Why do I have to retain it there? Shouldn't that happen by itself by declaring the property (retain)?
Or, in other words, why is the array autoreleased too early (assuming this is what is happening)?
2. When I assign a new array to self.documentListArray, is the old array automatically released? If I try to release it myself before getting a new documentList, it crashes too.
Thanks in advance for any reply.
EDIT:
Maybe I'm an idiot: I failed to mention that documentListArray is the data source for an UITableView (see the added line on top). I suspect that I am doing something wrong with populating the table view, and the array gets retained...? It does however crash on assigning the property, not on reloadData.
I go back to study if I use the UITableViewDataSource protocol properly. Thanks to everybody, your answers brought me hopefully on the right track. Will update when solved.
EDIT2:
It works now without retaining, and I think I understand why: I debugged extensively and found that Objects contained in Objects added to the array where nil. Particularly, deep down in encodeWithCoder I did not use "self" when assigning values. When decoding, those values where nil. Since I changed that, it seems to work.
I suspect that not assigning the new array caused the crash, but the TableView which would read the new array-even before I call reloadData. Which would lead back to Davids question of synchroneous access. Thank you all for your help.
The code you've shown it appears correct; it should not be necessary (or correct) to call retain yourself, as long as you are assigning the value to a property with retain semantics before the autorelease pool is drained. Are all the calls (getDocumentListSortedByDate, getDocumentList) happening synchronously, or are you doing any of this in the background? Double-check that you're assigning using the "self." ("self.documentListArray =") instead of just assigning directly to the instance var ("documentListArray ="); if you omit the "self.", the setter is bypassed.
No, don't free the old value before assigning; that's the setter's job.

Releasing an object but still able to use it

I had understood that once you release an object, you shouldn't use it as it will cause an error since it is not in memory anymore.
But reading thru this Apple guide, I found this code, and have also seen it before, but I would just move the [object release] to the end of my code, so as to avoid getting an error. But it seems that it is accepted and works. So, why does this work? How can it keep setting variables to dateAttribute after it's been released?
(Line 3 is the one in question):
NSMutableArray *runProperties = [NSMutableArray array];
NSAttributeDescription *dateAttribute = [[NSAttributeDescription alloc] init];
[runProperties addObject:dateAttribute];
[dateAttribute release];
[dateAttribute setName:#"date"];
[dateAttribute setAttributeType:NSDateAttributeType];
[dateAttribute setOptional:NO];
Got it from here: Creating a managed object model in code
There are few points we should discuss.
release does not always make the object deallocated. The object will be deallocated only at the "last" release, i.e. when the retain count drop to zero.
Despite of that, it is still hold true that you should not use the object after you release it, because it is possible that it might be deallocated already.
The NSMutableArray will retain the object until it is removed from the array, or the array itself be allocated.
The example take the advantage that the array will retain the reference when added, so the reference will not be deallocated yet after releasing dateAttribute. However, this is not a good style because its validity depends solely on the nature of the class NSMutableArray itself, and it breaks common rule that we should not use released reference.
Technically, this is bad style, however it does work.
NSMutableArray (the runProperties addObject) calls retain on the dateAttribute. Therefore, calling release does not destroy the dateAttribute (there is still one reference).
For readability and refactoring reasons, I would also place the call to release last.

unrecognized selector sent to instance 0x5d18d60... i'm going crazy!

I'm going crazy with this my little app... Please help me!!!
this is the source code of the app: Smoking.zip
It only saves a .dat file with an NSMutableArray.
Now, the first time you will launch the app, try to click the cigarette button sometimes: Everything should working fine.
Ok, now close the app, re-open it, and click again on the button. This time the app will crash with the "unrecognized selector sent to instance 0x5d18d60" error.
I was sure the problem was in saving the data, because when i commented the line "[theData writeToFile:dataFilePath atomically:YES];" in the "saveData" method the error disappeared.
Later i discovered that it appears again if i try to read the data from the NSMutableArray.
Please take a moment to check my project and help me, beacause i'm going crazy about that!!
You crazy man, it took quite some time to find these lines:
Cig *oldCig = [mainDelegate.smokeArray lastObject];
...
[oldCig release];
Why are you doing that? You effectively reduce the retain count of the last object in the array to 0. When saving, it is happily saved, with its retain count of zero.
On de-serialization, the decoder will retain any (sub) element it decodes, so the retain count of this last object will, for a brief moment, be 1. Then, on release of the decoder, it releases all elements, and poof goes the last Cig object. When getting that object from the array, you get a pointer pointing to something totally different, and the app crashes.
You should read up on memory handling. lastObject just returns a pointer to an object in the array, without retaining it for you, so you don't have to release it. Furthermore, for functions like
- (NSArray *) quando
try to return an autoreleased array, by calling autorelease before returning:
NSArray *newArray = [[[NSArray alloc] initWithObjects:year,...,nil] autorelease];
Then your other code doesn't have to think about releasing it. And release the dateFormatter. Anything you alloc, retain, copy or new, you must release or autorelease!
easy. On SDK 3.2 and 4.0 you need to make your button functions like this.
// Note it takes one argument UIButton.
- (IBAction) smoke:(UIButton * ) button {
Change this in your .h file and .m file, you dont haven to change anything else. Worked for me.

NSArray release crashes with EXC_BAD_ACCESS

Another puzzler. I have an NSArray (iTours) containing 3 Objects and a retain count of 1.
This Array is released in the object dealloc method as follows:
- (void)dealloc {
NSLog(#"retain count für iTouren: %d", [iTours retainCount]);
[iTours release]; // triggers EXC_BAD_ACCESS
[super dealloc];
}
The console window shows the following message (no further details):
Program received signal:
“EXC_BAD_ACCESS”.
any clues what's going on? What did I miss?
Most likely, you are over-releasing iTouren and, thus, that call to release is causing the crash. That is, iTouren is already deallocated by the time you release the containing array and when that containing array sends release to the already deallocated iTouren your app crashes.
(Of course, iTours might be the object that is already deallocated. In any case, it is an over-release problem.)
Turn on zombie detection and see if that barfs up the specific problem.
Note that the number returned by retainCount is useless. Absolute retain counts are an implementation detail and will often be a specific value that seems like nonsense.
In this case, the final release of an object does not decrement the retain count. Why? Because that'd be a wasted cycle when the object is about to be deallocated anyway. It would be impossible for retainCount to return 0 because, by definition, an object with a retain count of 0 has already been deallocated and, thus, is no longer a viable message receiver anyway.
Never, ever, ever, rely on the retainCount method.
Ever.
Did I mention ever?
One is iTours the other one iTouren. You are logging a different object than that being released.
Assuming iTours and iTouren are different objects (namely that iTours is an NSArray containing iTouren), you've over-released iTours; maybe you had a failure in a setter and iTours was released without being reassigned and retained.
Be sure that your array does not contains itself. Releasing an array also release every object in it. Of course, if this is the case, the array should have a release count of two before you release it because arrays retain their elements.
A released and deallocated object will have a retain count of 1. A non-released object about to be released and deallocated will also have a retain count of 1. (Assuming memory is ever allocated for the object, some strings and numbers are never allocated, implementation detail that changes per Mac OS X release)
When an object is deallocated in current versions of Mac OS X, the memory is marked as free, nothing about the object is modified (unless you tell it to be). Thus, the retain count remains identical to the retain count before it was released.
This is the make a mistake and DIAF approach that help makes Mac OS X so solid.