I have what I think is a weird error but of course i'm relatively new to iPhone Development so it's possible that it's not all that weird after all.
I have an array (NSMutableArray) of objects that I am keeping track of (one is added to the array every time the user touches a button) what I'm trying to do is when the array reaches a certain value I add the new object to the beginning of the array and then remove the last object in the array. When I step through my code everything works the object is removed but then the app just crashes...the debugger isn't on any line of code when the app crashes and there are no loops or timers in the app yet so I can't think of anything else that is running.
here is the code that is executed right before the crash
if([objectArray count] > 10)
{
MyObject *objectToRemove = [[MyObject alloc] init];
objectToRemove = [objectArray objectAtIndex:10];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
}
the main point of this code is that everytime the user touches a button an object is added to the screen and displayed, and then when the number of objects reaches 10 and the user touches the button again the first object that was added is removed and the new object is displayed. If I comment out the removeObjectAtIndex line everything works as intended but the array continues to grow.
I've also tried removing the object after the UIview is removed and the app behaves the same way. If I try to remove an object from the array at a different index (I.E 3) the app doesn't crash but it doesn't give me my expected result. but like I said the code runs fine and when I check the count of the array before and after the execution of the line the value is 11 and 10 respectively.
Any help you can provide would be appreciated,
BWC
I don't think this does what you think it does. Going line by line:
MyObject *objectToRemove = [[MyObject alloc] init];
You allocate a new object of type "MyObject.
objectToRemove = [objectArray objectAtIndex:10];
You overwrite your local MyObject pointer with whatever was at index 10 in objectArray. The objectToRemove that you initially allocated is now leaked.
[objectArray removeObjectAtIndex:10];
Now you've removed the object at index 10. When you remove it from the array, it is released and its reference count is decremented. This may (or may not) cause it to be deallocated.
[objectToRemove removeFromSuperview];
Now you are sending a message to the object that was previously in the objectArray. If the object was deallocated by the previous line of code, I would expect a crash.
Putting aside the leak in the declaration, you might be able to do this:
objectToRemove = [[objectArray objectAtIndex:10] retain];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectToRemove release];
This would prevent the object from being deallocated out from under you, if that's why the crash is happening.
One way to determine if my scenario is what is happening is to turn on NSZombies, which you can do by setting the environment variable NSZombieEnabled to YES.
How about:
MyObject *objectToRemove = [objectArray objectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectArray removeObjectAtIndex:10];
removeFromSuperview will decrement the retain count once on objectToRemove. (removeFromSuperview implies that it was added to the view previously and that had retained it and incremented its retain count by one). removeObjectAtIndex will decrement it again. But in this order, you most likely will not be releasing it when you still need it around. (Of course, adding to the array also retained it so the order may not be that important).
Related
Just a quick question:
I've got an array, name it sourceArray. This array has dictionaries in it.
I've got a retained property, which is used to hold one item from the array.
When I set the property from the array it works fine.
But, when I set it with an other item, and back to the first, I got a bad_access exception, since the first item has been released.
My question is why? In the source array, items are retained, and when I set the property it retains to. When I set it again, the setter release the old value, and retain the new. I guess... But apparently it doesn't work in this way.
Example:
[self setProperty:[sourceArray objectAtIndex:0]];
[self setProperty:[sourceArray objectAtIndex:1]];
[self setProperty:[sourceArray objectAtIndex:0]]; ---> Exception
I've got it.
In other place, I've a line:
property = [sourceArray objectAtIndex:0];
It looks like when I use the set method, the old value get released, no matter that it hasn't been retained (since in the first time, I didn't use the set method).
My fault...
OK, so I am halfway through developing an iPhone app and I keep stumbling when it comes to memory management.
I have tried a number of times to understand this with very limited success. I consider myself to be above average intelligence, but this stuff just eludes me, despite repeated searches and reading of Apple documentation
Lets say I have a picker that I am creating - so the code goes
UIPickerView *patientPicker = [[[UIPickerView alloc] init]retain];
//more code here
[self.view addSubView:patientPicker];
So then I do a couple of different things with my picker.
The picker only appears when a segmented control button is pressed. The segmented control dictates which array of data is used to populate the picker.
However, when I change segmented control, I find that it displays a new picker on top of the old picker, rather than changes the data in the current picker.
i.e. segmented control is patient age or weight. If age is selected a picker of ages appears, and the same if weight is selected the picker of weight appears. However if one of the pickers is already present, then clicking on the alternate segment doesn't change the data, it just adds another picker onto the view.
My problem comes when I try and hide the picker as the old picker is still underneath, and I can't hide the old one.
So when I click a button to remove the picker, the old picker is still present underneath.
I have tried
[patientPicker removeFromSuperView];
but when I try and rebuild my picker I am advised that patient Picker has been deallocated???
The same goes for
[patientPicker release];
I know that someone would be able to tell me the simple answer, but what I really want is a really simple/dumbed down explanation of memory management so that I don't have to ask again.
Pretend I am 7 years old!
Thanks
Bob
UIPickerView *patientPicker = [[[UIPickerView alloc] init] retain];
Here you don't/shouldn't do a retain. The init call already implies that the caller is responsible for the create object (in other words, init has a retain implied). You need a release for every init and retain.
I reckon that you doing an alloc/init each time the segmented control changes selection. What you can do is, In the vieDidLoad, do this:
UIPickerView *patientPicker = [[UIPickerView alloc] init;
//more code here
[self.view addSubView:patientPicker];
patientPicker.hidden = YES;
[patientPicker release];
When the selection is made on segment control, set the hidden property of the picker to NO and set the datasource according to the selection
this sounds like a job for a tool... yes! hit Build and Analyze and remove every issue =) but understand why the static analyzer flags your program, and understand that there's quite a bit it can catch, yet quite a bit that it can't prove is a reference count imbalance and will not flag these. then run with leaks instrument and fix all issues, etc. and if you run into deallocated instances, run zombies instrument and fix all issues.
anyways, there's more to it! here are some points to your code.
UIPickerView *patientPicker
= [[[UIPickerView alloc] init]retain]; // << do not retain here. alloc
// returns an object you must release
[self.view addSubView:patientPicker]; // << self.view will retain its subviews.
// ok, that makes sense that the view
// would want to hold onto a reference to
// ensure the view is not destroyed
// while it's still a subview.
[patientPicker removeFromSuperView]; << the superview will release its subview
[patientPicker release]; << your life will be easier if you use the accessors
when you're dealing with reference counting, you need to hold a reference to use an object. so, let's take autorelease pools out of the equation. using autorelease only when needed will help you learn, and make some of your issues local to the callsite -- avoid calling autorelease where possible while you are learning.
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a release]; // << I hold 0 references
[a length]; // expect bad things
now let's illustrate autorelease pools:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a autorelease]; // << add a to pool. when pool is destroyed a release message will be sent to a
[a length]; // ok. a is still alive. its release message is deferred until the pool is destroyed
[pool release]; // pool's reference count has reached zero. pool will call [a release]. pool will be destroyed.
[a length]; // expect bad things
Two rules of memory management:
If you new, alloc init, retain, or copy (N.A.R.C.) an object, you have to release it.
When the name of a method starts with any of those words, it means it's being created for the caller, who has the responsibility to release the object when he is done with it. Otherwise the method returned is not owned by the caller, and he has to indicate he wants to keep it calling retain on the object.
Note that the first rule results in the second. A method creates an object (therefore it is responsible for releasing it) but if the outcome of the method (the returned object) survives the execution of the method (so it can be handed to the caller) all the method can do is autorelease the object. An autorelease adds the object to the autorelease pool, which will release the object at some point in the future.
Example:
[[MyObject new]; // 1)
[NSString initWithFormat:#"%d",1]; // 2)
[NSString string]; // 3)
1) Name contains new, so it will need release.
2) Name contains init, so it will need release.
3) Name doesn't contain any of the NARC words so you know it returns an autoreleased object. This means you need to retain it if you intend to keep it, and if you do, you will need to release it later. Otherwise just use it and forget about it.
Couple of tips:
Try to retain/release symmetrically so you don't lose track of what to release and where. Example: if you retained on init, then release on dealloc. Same for viewDidLoad/viewDidUnload.
If you can choose, don't abuse autorelease when memory is a concern so you recover your memory as soon as possible. Abusing autorelease is also a sign you don't understand memory management.
Your example (same thing Justin told you):
UIPickerView *patientPicker = [[UIPickerView alloc] init]; // 1)
[self.view addSubView:patientPicker]; // 2)
[patientPicker release]; // 3)
1) We call alloc init so you know this object will need release once you are done with it.
2) addSubView calls retain internally. Why? When you receive an object and you intend to keep it, you express that intention calling retain, which also gives you the responsibility of releasing it. When a UIView is released, its implementation also releases its subviews to balance the retain done before by addSubView.
3) We won't be using it anymore so we call release. Now self.view is the owner of the object.
As an exercise, try to implement your own setter and getter for a variable and run Build and Analyze to see what Xcode thinks about it.
Focus on Ownership, if you have ownership; you have to release object. If you don't have ownership don't dare to release it.
id myObject;
myObject = [abc retain] => you are the owner
myObject = [[abc alloc] init] => you are the owner
myObject = [abc copy] => you are the owner
myObject = [[[abc alloc] init] autorelease] => you are not the owner
(everywhere you put autorelease you loose the ownership)
myObject = [abc xxxWithYYY] => you are not owner
(as a convention, a method returning object always give an autorelease object)
There will be some more similar conventions where you can identify OWNERSHIP; I just jotted down what I can recall now.
In the code below, PersonListArray is an NSMutableArray and I'm getting the list of persons from the sqlite DB and adding it to my array.
Person* tmpPerson = [[Person alloc] init];
tmpPerson.personName = #"Mike";
tmpPerson.personEmail = #"mike#mike.com";
[PersonListArray addObject:tmpPerson];
[tmpPerson release];
Even though I'm releasing the Person object here, its giving a memory leak which I'm guessing is due to the array holding a reference count to it. I'm using the array elsewhere in the program and then releasing it for sure.
What's the best practice to create new objects for an array and not run into this issue?
In the dealloc method where i release the array
-(void) dealloc{
[PersonListArray release]; // this contains the numerous Person objects
[super dealloc];
}
should i manually release them like this instead ?
-(void) dealloc{
for (int i = 0; i<PersonListArray.count;i++)
{
Person * tmpPerson = [PersonListArray objectAtIndex:i];
[tmpPerson release];
}
[PersonListArray release];
[super dealloc];
}
The code you are showing us is correct and contains no leaks. The last section is wrong, though, and would case your program to crash because you are releasing Person objects you no longer own.
Your code, as initially implemented, is correct. An array retains onjects added to it and releases them either when they're removed from the array or when the array is dealloced. No need to go through the array yourself.
What means are you using to detect the leak? If it's Instruments then you may be misunderstanding what it is telling you. When it detects a leak, it can show you where the memory was first allocated. It can't show you which object is responsible for the leak. I would therefore guess the given dealloc method is never called (because that object is leaked) or that someone else retains the array and doesn't release it. Try putting an NSLog in dealloc to ensure that it is occurring; as a run once test you could try logging PersonListArray after releasing it — if that doesn't cause a memory exception then almost certainly someone else has retained it.
[REMOVED: my original text "Try adding an NSLog of [PersonListArray retainCount] to your dealloc to figure out which is the case."; see comment from bbum below]
The most common cause of accidental additional retains is #property/#sythesize properties that are set to retain but for which a matching release is not added to dealloc.
Somewhere else in your app, you probably call [PersonListArray objectAtIndex:n] and pass it around to various other parts of your app. One of the other parts of your app is probably leaking it.
If you're using leaks, click on the particular "type of leak", then click on the memory address, and it'll show you the alloc/free/retain/release/autorelease history of that memory address. If you enable the detail view (Cmd-E I think), you'll see stack traces for all of those as well. Look for something that's doing a retain but not a corresponding release. (It's a bit difficult when things are retained by multiple autoreleased arrays...)
What's the right way to do this?
I have an array that I will use on several methods. I will add objects to it, get values, replace values, etc.
Today I do this:
I declare it on .h, using something like
NSMutableArray *myArray;
as soon as the application starts, I declare it on .m doing something like
myArray = [[[NSArray alloc] init] retain];
If I don't add the retain the array will be released at some point and the application will crash. But allocating the array at the beginning of the application and left it "open" without releasing it will make instruments cry, pointing the finger at me, calling me a "leaker"...
How to solve that? Is this the correct way to do that? how do you guys do stuff like this?
thanks
alloc implicitly sets the retain count to 1. By sending the retain message you're incrementing the retain count to 2. In order for the object to be deallocated you would then need to release it twice. Failure to do so would result in a memory leak.
Ideally you should create the object in your init method using [[NSArray alloc] init] and then release it in your dealloc method like so:
- (void)dealloc {
[myArray release];
[super dealloc];
}
You might also find this article useful: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
One more thing: You declared myArray as an NSMutableArray but instantiated it as an NSArray. Perhaps that's causing the crash.
You should not retain the object you've just created. You already own it. If, as you say, "the array will be released at some point and the application will crash," that is the code you should change. Your code shouldn't be releasing an object that you still want to keep around.
I'm trying to grope my way through Obj-C to build an iPhone game.
I'd like to build an array of objects for later use. Here's what I tried:
NSMutableArray *positionIcons;
[positionIcons insertObject:annotation atIndex:0];
positionIcons = [NSArray arrayWithObjects:annotation, nil];
The insertObject line leaves the count at 0. However, the next line correctly inserts the object (and count moves to 1). What gives?
You need to initialize positionIcons, change the code to:
NSMutableArray *positionIcons = [[NSMutableArray alloc] init];
[positionIcons insertObject:annotation atIndex:0];
positionIcons = [NSArray arrayWithObjects:annotation, nil];
#msaeed has the right answer, but it's worth taking a little more time on that code fragment, because it leaks memory.
The iPhone doesn't support garbage collection, so it's important to understand how Objective-C's semi-automatic memory management works. Apple has a good reference here, but the gist of it is that you are responsible for maintaining the "retain count" of your objects. When initializing an object with an -init instance method (such as [[NSMutableArray alloc] init] in your example, but also any other method starting with "init", like [[NSMutableArray alloc] initWithCapacity:42], the newly-initialized object has a retain count of 1. Subsequent calls to that instance's -retain method increment the retain count, while calls to the instance's -release method decrement the count. When the count reaches 0, the object is deallocated, and further attempts to send it messages will result in null pointer exceptions.
In the case of #msaeed's corrected code, here's what's happening, by line:
A new instance of NSMutableArray is allocated; the -init method is called, which initializes the instance and sets the retain count to 1. The positionIcons pointer is then set to the address of this new instance.
The -insertObject:atIndex: method is called on positionIcons, and all is well (also, by convention adding an object to a collection like NSMutableArray increments that object's retain count, the idea being that the collection now has, in some sense, "ownership" of that object, and doesn't want it to be deallocated from underneath it).
A new instance of NSArray is allocated, and the positionIcons pointer is then set to the address of that new instance. Because the retain count of the NSMutableArray from line one is still 1, it will not be deallocated, and since you've lost your reference to it, you can never call -release on it to clear it out of memory. There's your leak.
By convention, there's a difference in how you manage objects that are initialized with -init instance methods versus class methods like +arrayWithObjects: (in the first case, you have to release the object yourself, but in the second case, the object has already been sent an -autorelease message and will be deallocated on the next pass through the program's runloop unless you call -retain on it.