Objective-C Object gone due to memory management in NSMutableArray - objective-c

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.

Related

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.

How do I fix a memory leak?

After doing a long profile test, I found that in one of my ".m" file memory leak occurs in the viewdidload section. i checked and the xcode highlighted the part where i have initialized picker arrays with values. my program uses pickers for user input. and i have 3 5 different views in my program. the first is a disclaimer ,the second is a menu where the user can choose the type of calculation he/she wants to do. each calculation requires certain inputs which the user enters from a picker. for eg. one of the view has 5 inputs which are handled by 5 different uipickers with seperate arrays for holding the values. these arrays are initialized with the values in the viewdidload method of that view. here is what i found after running the test:
...................................................................................................
This is my first time developing an app and i'm kinda confused about what to do. Any help would be appreciated.
Objects in objective c have a retain count. If this retain count is greater that 0 when the object goes out of scope (when you stop using it), it leaks.
The following things increase the retain count
[[alloc] init]
new
copy
[retain]
adding an object to an array
adding an object as a child (e.g. views)
There are likely more, but you don't appear to use any others in your code
The following decrease the retain count
[release]
removing an object from an array
if you dealloc an array, all of its objects are released
You should go through your code and ensure each of the retains or additions to an array are matched with a corresponding release. (You can release member variables in the dealloc method).
EDIT: Jeremy made a valid point that my answer doesn't
Once you add an object to an array, it takes ownership and will release the object when it is done with it. All you need to do is make sure you release anything you own according to the memory management rules
There are also autorelease objects, have a look at this example;
-(init){
...
stagePickerArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 3; i++)
{
//this string is autoreleased, you don't have call release on it.
//methods with the format [CLASS CLASSwithsomething] tend to be autorelease
NSString *s = [NSString stringWithFormat:#"%d", i);
[stagePickerArray addObject:s];
}
...
}
I think the only thing you are missing is a call to release in your dealloc method
-(void) dealloc
{
[stagepickerarray release]; //Do this for each of your arrays
[super dealloc];
}
The leaks tool will only tell you where yo allocated the objects that it thinks leaks. So, it's telling you, for instance, that
NSString* answer = [NSString stringWithFormat: ...
allocates an object that is never deallocated. Now, -stringWithFormat: gives you an object that you do not own and you don't seem to retain it anywhere. Therefore, you do not need to release it, so it can't be leaking by itself.
That means something else that you do own must be retaining it and you never release that something else. The prime suspect would appear to be stagePickerArray. Check that you are releasing stagePickerArray somewhere. If it's local to -viewDidLoad it must be released or autoreleased before the end of that method. If it's an instance variable, it must be released in the class's -dealloc method.
In Objective-C you need to take care of the retain count of allocated memory. If you don't need it -> release it.
Whenever you alloc an object, it will return an object with retain count = 1.
By using retain, the retain count gets incremented,
by using release, the retain count gets decremented.
Whenever the retain count is equals 0, the object will be destroyed.
So whenever you want to use the object somewhere else you need to retain it. So you make sure that the object is not deleted after the other 'person' (or whatever it used ;)) called release.
This was a very very very short description. Check the following guide
Memory Management Guide for iOS.
(Also you want to read something about ARC - Automatic Retain Counting - which is new in iOS5! ios5 best practice release retain

Allocating and releasing memory in iPhone Xcode 3.2.5, iOS 4.2.1

I am having a strange issue with tableview showing (null) when another ViewController comes and goes. I traced it down to the place that it reads the data to display to the tableview and found that it read null for data. To debug this, I need to understand how memory management really works for iPhone. I used the following code in my application delegate to create an array of objects. This was from a sample code which I modified for my app. My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
//Initialize the hardware array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.hardwareArray = tempArray;
[tempArray release];
This type of code seems to be very common for iPhone. The tutorials keeps saying for example, now that you passed the data to the controller you can release it. But my C and C++ experience tells me, if you release the memory it would be gone, and if its address is sitting someplace on the stack, its no good. Is the data being copied to another place rather than address being passed?
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens if one released modelName that is already nil?
-(void) setModelName:(NSString *)newValue {
self.isDirty = YES;
[modelName release];
modelName = [newValue copy];
}
My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
If the #property of the object is assign it would get released. But usually you use retain or copy as #property. And if the property is retain, the setter you call with self.hardwareArray = ... (ie [self setHardwareArray:...]) will retain the object. If the property is copy it will create a new object that is retained already.
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens is one released modelName that is already nil?
In objective-c messages sent to nil are perfectly legal. No need to worry, no need to check for nil.
If you release an instance variable outside of dealloc you should set it to nil after releasing it.
release doesn't do what you think - it isn't equivalent to free() or delete and doesn't cause the memory to be gone.
It really is important to fully understand the few simple memory management rules before you do any coding. It's not difficult, the best resources are the Apple Developer Guides, like this one
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html%23//apple_ref/doc/uid/10000011i

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.