Retaining objects in game iphone - iphone

I am trying to create a game and have run into what is probably a pretty easy problem to solve.
As the player goes through the game, many objects (Vehicle) will be added and removed.
The Vehicles get added to an array called currentVehiclesMutableArray.
My problem is I cant figure out how to retain a Vehicle so that it remains in the array until I am finished with it.

A NSMutableArray automatically retains anything you add to it.
In fact, you have to make sure you release an object after you added it, or you'll have a memory leak.
For example:
Vehicle *vehicle = [[Vehicle alloc] init];
[mutableArray addObject:vehicle];
[vehicle release]; // you should release it, because it was retained by the array
// at this point, mutableArray holds your vehicle object, and is retaining it

Related

How do memory management properties affect cells of an array?

In my iPhone development book, I'm seeing some strange coding examples in regard to what an array does when objects are added to the array and when the whole array is released. One code example has the following properties on an instance array:
#property (nonatomic, retain) NSMutableArray* myArray;
The author adds an object to the array and, immediately after, releases his pointer to the object. Won't the array cell now point to garbage data? Unless, behind the scenes, the array cell retains the object when added.
SomeObject* someObject = [[SomeObject alloc] init];
[self.myArray addObject:someObject];
[someObject release];
The author also releases the the pointer to the array without first going through each array cell and releasing the individual objects. This is a memory leak unless, behind the scenes, each cell is sent a release message;.
- (void)viewDidUnload {
self.myArray = nil;
[super viewDidUnload];
}
Unless, behind the scenes, the array cell retains the object when added.
Yes, this happens.
... unless, behind the scenes, each cell is sent a release message.
This also happens.
You have answered your own question.
Here is a quote from Collections Programming Topics:
And when you add an object to an
NSMutableArray object, the object
isn’t copied, (unless you pass YES as
the argument to
initWithArray:copyItems:). Rather, an
object is added directly to an array.
In a managed memory environment, an
object receives a retain message when
it’s added; in a garbage collected
environment, it is strongly
referenced. When an array is
deallocated in a managed memory
environment, each element is sent a
release message.
Unlike in C or C++ where you constantly worry about whether to delete an object or not for the fear of it is still being used somewhere else, Objective-C (or rather it's actually Cocoa SDK) uses the mechanism of reference counting or ownership.
You might already know how it works but you need to also know that in Cocoa, if an object A needs to use another object B it should own (i.e. retain) it. That object A should not rely on some other object C already retained B, because it cannot know when C releases it. So in your case, since NSArray needs to use all objects added to it latter during its lifetime, it needs to retain all the objects. And because of that, when the array is de-alloc-ed, it needs to release them.
This concept of "you need to retain what you want to use latter" is very important when you are dealing of lots of objects.
There are several places in apple development guides that explain that is a good practice to take the ownership of an object (send a retain message) if you plan to use it later. You should do it so that the object is not destroyed while you still might need to access it.
Considering that, you were right assuming that the NSArray retains the object when it is added to the collection, as it still might try to access it afterwards.
You can check the Memory Management Programming Guide
When you add an object to a collection such as an array, dictionary, or set, the collection takes ownership of it.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW3
or the Collections Programming Topics for more details
... In a managed memory environment, an object receives a retain message when it’s added.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Arrays.html#//apple_ref/doc/uid/20000132-SW1
You're right on the first point. When an object is added to an array, the array retains the object. Thus, for an object that has been previously retained, it is necessary to release it after adding it to the array or you can end up with a memory leak.
Likewise, when an object is removed
from an array, the array releases the
object. So, if you want to keep it,
you'll need to retain it.
When an array is released, as you
surmised, the array will release all
the objects it contains. Thus,
releasing each object individually is
not necessary and, in fact, would
raise an exception.
Finally, regarding the line of code
in -viewDidUnload that you quoted:
self.myArray = nil;
This works properly with regard to memory management as long as the myArray property was synthesized as follows:
#synthesize myArray;
Synthesizing creates a setter that effectively does the following:
- (void)setMyArray(NSMutableArray *)anArray
{
if (![myArray isEqual:anArray]) {
[myArray release];
myArray = anArray;
[myArray retain];
}
}
So, when called, the above setter will first release the old array (as long as it's not the same object as the new array.) Then, it will retain the new array, which in this case is nil. Note that retaining nil will just do nothing, and won't trigger an error.
Of course, if you don't synthesize the myArray property, or if you override the setter, you will have memory problems unless you also release the old value & retain the new in your setter.

Cocos2D - NSMutableArray of CCSprites

I tried storing some CCSprites in a NSMutableArray, but the game crashed immediately, I'm guessing it's a memory problem, and I'm also guessing that CCSprites are autorelease objects?
So, how would I store multiple CCSprites in a NSMutableArray?
The purpose I wanna do this is store for example all enemies in an array, and then loop through them in my timestep function and update their positions and whatnot.
What I tried to do:
NSMutableArray *enemies = [NSMutableArray array];
[enemies addObject: [CCSprite spriteWithFile: #"hello.png"]];
It crashes when I try to reach the sprite using -objectAtIndex:
The array is autoreleased. If you try to access it later in another context, it probably already died. So you either retain it or don't use the convenience array method, but [[NSMutableArray alloc] init] explicitly.
Or store it in a retained property (be sure to use the setter method in that case, i.e. self.ivar = enemies;
Whichever way you go, be cautious not to "over-retain" your array, i.e. using alloc/init and the retaining setter, or your array will never get freed again (more correctly it will only get freed with a "buggy" double release).

NSMutableArray removing the object but then app crashes

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).

insertObject doesn't add object?

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.

iPhone cocos2d sprites in array, memory problems

I'm trying to keep track of my sprites in an array, add and remove
them from layers, and then finally clear them out of the array.
I'm using the following code:
Sprite * Trees[50];
Layer * Forest;
Forest = [Layer node];
Forest.isTouchEnabled = YES;
[self addChild:Forest z:30];
// do this a bunch of times
Trees[0] = [[Sprite spriteWithFile:#"mytree.png"] retain];
[Trees[0] setPosition:cpv(240,160)];
[Forest addChild:Trees[0] z:5];
And then when I want to destroy a tree I use:
[Forest removeChild:Trees[0] cleanup:YES];
[Trees[0] release];
My problem is that when I look in Instruments, I'm never reclaiming
that memory, there is never a drop back down. I thought that by
releasing the sprite it would free up the memory. Am I doing this
completely wrong?
I know that when you are using the simulator with cocos2d, the memory doesn't look like it's being released, so you have to run it on the device to get an accurate picture of what's going on.
There is a good discussion here about cocos2d and memory.
What I've noticed is that everything that you create and retain must be released, but it isn't released from memory until I do this:
[[TextureMgr sharedTextureMgr] removeAllTextures];
That will release the memory.
Here's a bigger example:
Sprite * sPopup = [[Sprite spriteWithFile:#"popup.png"] retain];
sPopup.position = cpv(240,440);
[self addChild: sPopup z:2];
[sPopup release];
Then, when I'm done with sPopup in another function I have this:
[[TextureMgr sharedTextureMgr] removeAllTextures];
and the memory is freed.
My suspicion is that you are "over" retaining:
Trees[0] = [[Sprite spriteWithFile:#"mytree.png"] retain];
If Trees is a local variable in a function you do not have to retain in that case if spriteWithFile is returning a Sprite with an autorelease.
The section on delay release in the apple documentation discusses this further. The long and short of it is that the receiver of the autorelease is guaranteed to have the object be valid for the duration of its scope. If you need the object beyond the scope of the function (e.g. Trees is a property of a class) then yes, in that case you need a retain (or just synthesize a property configured to retain).
By issuing the extra retain, it is likely that your retain count is always too high (never reaches 0) and hence your object is not garbage collected.
For good measure, I'd suggest reviewing this paragraph as well that talks about the validity of objects.
Even though you call [Trees[x] release], I believe you still need to 'delete' the item from the array, like Trees[x] = nil or something, as the array itself is still containing the object.
The 'retain' in the Sprite creation is also not necessary, as [Forest addChild:z:] will place a retain on it as well (afaik).