Difference in setting arrays - objective-c

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.

Related

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

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.

Accessing a NSManagedObject causes EXC_BAD_ACCESS

Update: Tidied up question and made it a bit clearer
I am getting EXC_BAD_ACCESS crashes on a NSManagedObject.
I have a Sentence managed object that I pass to a modal view (addStoryItem) like so:
addStoryItem.sentence = (Sentence*)[fetchedResultsController objectAtIndexPath:indexPath];
AddStoryItem is set to retain Sentence:
#property (retain) Sentence *sentence;
Sometimes the user needs to do something that shows another modal (on top of addStoryItem) - which doesn't affect this object, but it does take a copy of a NSMutableSet - sentence.audiosets
If I they do view this modal I get an EXC_BAD_ACCESS whenever I try to access or set the sentence object or its properties, once the user is returned to addStoryItem
There is a current managed object context & fetched results controller
everything works fine unless I show that modal view controller (which, afaik, doesn't have anything to do with the sentence object)
Zombies is on, but it doesn't tell me anything (BRAINS?)
Here's a simple summary of what goes on:
user selects row in tableview
I get object from table and set the modal's sentence property then display the modal with the fetchedResultsController
I display a string, image and set a nsset from the sentence to ui aspects of the modal
if the user needs to modify the nsset they display another modal, with a copy of the first nsset (which doesn't change or access the sentence object)
if I try to set a property in the sentence after closing the 2nd modal (or NSLOG sentence) - EXC_BAD_ACCESS.
As far as I'm concerned I own sentence. Other properties of addStoryItem are still hanging around in memory - but sentence isn't there when I try to get to it. Yes, I release sentence in addStoryItem's dealloc - but that's not being called (I have a log statement in there).
Can you help? Happy to provide more code or info. Pretty frustrated!
You are creating a new sentenceToUpDate in your didSelectRowAtIndexPath:. Surely, this reference will be forgotten as soon as you are out of that method.
Rather, you should assign the retrieved object to your retained property, like this:
self.sentence = [fetchedResultsController objectAtIndexPath:indexPath];
Now the instance should be retained as expected.
Another possible culprit is your copy of the NSSet. Try creating a new NSSet to make sure you are not effecting the entity:
NSSet *setToBePassedToModal = [[NSSet alloc]
initWithSet:entity.toManyRelationship];

ARC: Memory does not get reclaimed?

I am working on an iPad (only) app and I stumbled across a weird problem. The app gets terminated after a memory warning on iPad 1 but works fine on iPad 2.
I am using ARC and targeting iOS 5. I use nibs and most of my assets are displayed using UIImageViews. I also have a few hundred buttons and plenty of gesture recognizers... I re-watched the WWDC11 videos (sessions 323 and 322) on ARC and I don't seem to be doing anything special.
The app is UIImage intensive, I am doing lots of animations using UIImage. I am however using the initWithContentsOfFile constructor rather than the imageNamed call. I'm trying to prevent the images from being cached by the system.
I'm also using GCD to schedule sound effects and to animate views. I'm always doing this on the main thread.
The app uses a UINavigationController that never has more than 1 UIViewController on it's stack. I can confirm that this is true because the didReceiveMemoryWarning only gets called on the current view controller (I'm logging the call).
The thing I don't understand is why Instruments is reporting high numbers (as if the view controllers don't get deallocated) in both the Allocations and VM Tracker instruments. The Allocations instrument shows a small drop when I navigate from one view controller to another (which is what I expect) but the VM Tracker Instrument shows that the Dirty Size is not dropping when I do the same thing. Eventually the app uses too much memory and gets terminated (on iPad 1). When I get memory warnings on the iPad 2 the app does NOT get terminated though...
It feels as if my images, sounds or views don't get destroyed and the memory does not get reclaimed... My object hierarchy is very basic and there should not be any retain cycles of any sort. I don't even have simple delegates...
Do you have any suggestions? I really don't want to release this app only for the iPad 2 or newer... It's an app for kids and it would be a pitty... I'd be so much happier to learn that I'm doing something wrong, as I really want to make sure this app is the best it can be...
Cheers,
Nick
There are ways to say, 'optimise' your objects by setting their properties to nil when certain things aren't needed -- so while you can't write a dealloc method anymore, you can do self.object = nil (when pertinent) which ends up doing something like this in a non-ARC world for an 'retain' (i.e., strong) property:
- (void)setObject:(id)newObject
{
[object release]; // send release message to current object
object = newObject; // set reference to newObject
[object retain]; // send retain message to newObject
}
Now while in ARC you don't/can't write retain/release yourself in your code, the compiler inserts these calls for you, meaning that in practise, setting a property to nil would do this in the above example:
[object release]; // send release message to current object
object = nil; // set reference to nil
[object retain]; // send retain message to nil (no effect)
Moreover, this is just the tip of the iceberg -- you should make sure that there are no retain cycles in your code, which could be resulting in objects leaking without recourse to their destruction. This means, that there may be places where you're using strong references to a property (i.e., an object), when you should be using a weak property. The difference being, that strong references are retained, and weak references are assigned, the former having its retainCount incremented and the latter resulting in a property assignment that looks like this if handwritten:
- (void)setObject:(id)newObject
{
object = newObject;
}
I don't like answering my own question but I figured it could be helpful to future googlers. I implemented my own UIImage based animation and I no longer use the animationImages property. Turns out my memory issues are now gone as I no longer need to store all the images in memory and I load them as they are required using a timer.
I actually think that rolling out a custom animation is beneficial since it allows for callbacks and more powerful customisation options.
Once I'm happy with it and I feel like it's ready to be shared I will post the class(es) on GitHub.

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.

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.