Why does calling removeAllObjects twice on NSArray cause app to crash? - objective-c

Using the following code snippet on NSArray one time works but if I call it a second time directly after the app crashes, I don't really understand why, I would have thought that it would blindly remove all objects if any in the array? If thats not true can I test with count > 0 before I run it to be sure the app won't crash or is there a better way?
[_transactionRowsRows removeAllObjects];

NSArray is imutable, you need to use NSMutableArray.

Related

Objective-C Object gone due to memory management in NSMutableArray

I have a problem regarding the memory management when adding objects to a NSMutableArray. Weird thing is that it's all working fine for the first 8 objects I add, but when adding a 9th, the application crashes when retrieving this object.
UploadStatus *status = [[UploadStatus alloc] initWithStatus:[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
andImageInProgress:nil
imageForSuccess:nil
imageForFailed:nil];
[self.delegate notify:status];
[status release];
This is being done on several places with different texts. But this object contains my status that I display in a UITableView.
In the notify method of the delegate I add the UploadStatus object to the NSMutableArray and I reload the UITableView that shows the objects inside that array.
The first 8 times I add a UploadStatus object to the array and reload the table, it shows correctly. But the 9th time I get the error [CFString retain]: message sent to deallocated instance 0x5c655c0. This error occurs when reloading the table in the cellForRowAtIndexPath method.
Weird thing is that it always shows that the objects inside the NSMutableArray are out of scope like in this screenshot:
Nevertheless if I fetch the item, convert it into the UploadStatus class and get the status from it, it all goes smoothly (for the first 8 objects).
Does anybody have a clue why it goes wrong after adding the 9th UploadStatus object to the NSMutableArray?
Thanks a lot for your help!
The problem is with this code:
[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
You aren't retaining the string, so it goes away on the next execution of the run loop. You're getting lucky with the first 8. They happen to not get overwritten for some reason, or possibly some other object is retaining them. But the 9th one isn't and you finally see the results of the mistake.
You need for the UploadStatus object to retain that string (and later release it).
I note that you're directly accessing your ivars in this block of code rather than using accessors. This is almost certainly the root of your problem (it is the #1 cause of memory management problems in ObjC). Switch to accessors and most of your memory management problems will go away.
You should also go ahead and run the static analyzer (Build>Analyze). It might shed light. The problem is likely not in the above code; it's somewhere that you're storing something, most likely in an an ivar.

Occasional EXC_BAD_ACCESS on NSUserDefaults access or synchronize

In a tab-based app when I switch between some tabs, I sometimes get EXC_BAD_ACCESS. It's not every time but if you flick back and forward a few times it eventually happens.
Defined in the .h:
NSUserDefaults *theData;
I've got this in viewWillAppear and viewWillDisappear:
[theData synchronize];
The line at fault gets called in a function at the viewWillAppear stage:
NSMutableArray *thisArray = [theData objectForKey:#"FriendsArray"];
I'm using NSUserDefaults to store a few dictionaries of data. This is populated by server calls, but there's no need for an internal database due to it being refreshed often. I am open to other ways of storing this data if that would be better.
I have tried a number of things like casting it (NSMutableArray *)[theData objectForKey:#"FriendsArray"]; or using arrayForKey and a number of other things with no improvement.
Any help or tips would be greatly appreciated.
NSMutableArray *thisArray = [theData objectForKey:#"FriendsArray"]; // retain
The way you try to make an array mutable is wrong. Also written like that, as suggested in the comments, you should probably retain that array.
Try init/alloc a new mutable array with objects like this :
NSMutableArray *thisArray =
[[NSMutableArray alloc] initWithArray:[theData objectForKey:#"FriendsArray"]];
Another method, -(id)initWithArray:(NSArray *)array copyItems:(BOOL)flag;, allows you to make a copy of the original objects.
Values in NSUserDefaults can not be mutable. You are trying to cast an immutable object into a mutable one. Try casting it into an NSArray, then cast that back into a mutable array. Should fix your issue.
There's no real reason for using
NSUserDefaults *theData;
Instead use this to recover a mutable array from UserDefaults:
NSMutableArray *thisArray =
[[NSUserDefaults standardUserDefaults] objectForKey:#"FriendsArray"] mutableCopy];
This gives you a RETAINED mutable array.
Note also that you only need to use synchronize when saving to UserDefaults, not when reading. You should synchronize pretty much after every save since your app can crash or be shut down by the iOS without you having a chance to synchronize.

Difference in setting arrays

In my app i need to change content of one NSMutableArray constantly. I used this method
myArray = [NSArray arrayWithObjects:object1,object2,object3,nil];
I did it several times and all went fine until i noticed that when i quit the view the app crashes. I changed the way of setting content of myArray to:
[myArray setArray:[NSArray arrayWithObjects:object1,object2,object3,nil]];
and that was enough to stop crashes and everything to work just fine. But i'm just curious about what's going on there deep down inside that makes the app crash when using first method (and by the way why it crashes not immediately but only when i pop the view off the stack?) and affects nothing when using second one.
In the first case, you're setting myArray to point to an autoreleased object. In the second case, you're telling myArray (presumably an existing NSMutableArray instance at that point) to replace whatever contents it has with the contents of another array.
You need to read up on the memory management rules.

EXC_BAD_ACCESS for NSMutableArray

I have a NSMutableArray with a few view elements.
I remove the view from the view hierarchy and then check the retain count of the array, it returns 1. I then send [array release] but the code dumps with EXC_BAD_ACCESS.
I see that there are elements in the array but still the code dumps.
Here is what I found during debugging. The array has all the 100 objects present (count on the array returns 100) and the contents when seen on a debugger returns "Out of Scope" for the elements. Also, since the array is in the view hierarchy, the following code reduces the retain count by two:
for (Liv *view1 in viewArray){
NSLog(#"view count = %d", [view1 retainCount]);
[view1 removeFromSuperview];
NSLog(#"view count = %d", [view1 retainCount]);
}
Do not use retainCount
It is useless for this kind of debugging; you are working with views in the framework's view hierarchy. There could be any number of reasons why the retain count goes up or down by 2, 10, or 42.
From the roundabout evidence posted so far, this appears to be a very straightforward memory management issue.
First, use "build and analyze" to have the llvm static analyzer check your code. Fix any problems it identifies.
Next, how is the array allocated? Where do you store it? Post all of the lines of code that declare or manipulate the array.
Finally, as Paul said, turn on zombies and see what happens.
There is an off chance that this isn't a retain/release issue, but there isn't any evidence to indicate that yet.
Did you retain or alloc the array? If not, you shouldn't be sending it a release.
EXC_BAD_ACCESS means you're sending a message to a object that's already been released. You (or some code somewhere) is releasing it prior to the part of your code where you're sending release (or removeAllObjects).
Basically, if your retains and releases are balanced, you won't get this error. We'll need to see some code before we can offer anything more than generic advice.
Using NSLog() and retainCount isn't the easiest way to debug errors of this nature. Assuming you're on Xcode 3.2.6, then try running your code via Run > Run with Performance Tool > Zombies. That should give you a good pointer as to which part of your code is faulty.

Small Memory Issue with Objective-C

I made a Mac OS X app that basically runs an NSTask. The thing is, I made a class called XXTask to handle a file, and a class called XXController to handle the drag and drops in the GUI and asking the XXTask to handle a file now and then.
I had made almost the same thing in the past, and it worked fine. This time, I added a delegate protocol, and made XXController the delegate of XXTask.
When XXTask fails, I ask the delegate to show a particular view, and thus call a method like this :
[delegate showView];
This works, but when trying to relaunch using the information I stored on the first launch, the app outputs errors. I used NSLog to see what exactly was wrong, and it seems like three instance variables (two NSStrings and one NSMutableArray) are (null).
These are the three instance variables :
NSString *curFilePath;
NSArray *lastArgs;
NSString *lastLaunchPath;
I create them like this :
curFilePath = filename;
// filename is an NSString passed to the method where I first create curFilePath
// the object passed to the method is a retained NSString (an instance variable of XXController)
lastArgs = [[NSMutableArray arrayWithObjects:curFilePath, [curFilePath stringByDeletingLastPathComponent], nil] retain];
lastLaunchPath = [[[NSBundle mainBundle] pathForResource:#"xxtask" ofType:#""] retain];
All three variables are null, but the XXTask object is not, as it can still execute code.
Errors :
2011-01-15 16:38:57.233 App[24179:a0f] PATH : (null)
2011-01-15 16:40:52.846 App[24212:a0f] LAST ARGS : (null)
2011-01-15 16:40:52.847 App[24212:a0f] LAST LP : (null)
2011-01-15 16:40:52.847 App[24212:a0f] Exception detected while handling key input.
2011-01-15 16:40:52.848 App[24212:a0f] *** -[NSCFArray insertObject:atIndex:]: index (1) beyond bounds (1)
When I don't draw the view by calling the delegate, everything is okay. I could put that view method in the XXTask class, but I'd rather find a neat explanation to this first. What happens when I call the delegate? (It's the first time I tried using delegate protocols)
Thanks for answering!
If this code looks horrible to you, I have two excuses :
I've been messing around for a long
time, read the memory management
docs and did the most silly things.
Reference-count memory management is quite new to me. I've
never made real big apps like this
one in the past, so I normally can't
do a lot wrong, but this time memory
is way more important.
If I understand this correct (it's pretty hard without any code) you store some information held in XXTask, right?
Do you retain/copy the data? If not, it gets released as soon the XXTask gets released.
If I misunderstand your question, I ask you to provide some code which demonstrates your problem. This makes finding errors much easier.