How to debug Cocos2d removal of sprite memory issue - ios

Have been working on a Tower Defense game for iOS for some time now. I am using Cocos2d (v0.99.5 not to sure). Recently started to get into some problems when I started remove sprites.
Have profiles using instruments and Zombies and here's what I get:
The code for updating the sprites (and also delete them) is located in update:
-(void) update:(ccTime)deltaTime {
if (paused)
return;
CCArray *childs = [textureAtlas children];
//Update all objects
for (GameSprite *tempChar in childs) {
if ([tempChar respondsToSelector:#selector(updateStateWithDeltaTime:andListOfGameObjects:andAtlas:)]) {
[(GameSprite*)tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:[textureAtlas children] andAtlas:textureAtlas];
}
}
//Remove dead projectiles
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Projectile.class]) { //(**)
if (![tempChar alive]) {
[tempChar remove];
}
}
}
//Remove dead towers
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Tower.class]) {
if (![tempChar alive]) {
[tempChar remove];
}
}
}
//Remove dead creeps
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Creep.class]) {
if (![tempChar alive]) {
[tempChar remove];
}
}
}
}
The remove method in GameSprite.m :
-(void)remove {
CCLOG(#"remove");
[self removeFromParentAndCleanup:YES];
}
The first block updates the sprites in the SpriteBatchNode/textureAtlas. The three remaining blocks checks the different objects if the should be deleted. (their alive-variable set to NO?). The reason I have three blocks of different types is due to the fact that projectiles have (weak) reference to creep they are shooting at and needs to be deleted before the creep.
So my problem is that it randomly crashes when a projectile hits a creep and the projetile (and all other projectiles shooting at that creep) are to be removed). The creeps reference count gets down to 0 but seems to still be in the childs array. Cause the error I get is:
*** -[NormalProjectile isKindOfClass:]: message sent to deallocated instance 0xff33e30
on the row I marked with (**)
Either the problem is with me understanding Cocos2d's removeFromParentAndCleanUP: or that my "solution" for handling the projectile-creep-relationship is bad from a memory point of view. Any ideas on how to solve this or debug it further?

Your fast enumeration techniques for going through the array are casting all your sprites in that array as a custom subclass of CCSprite, then checking to see if they are instead a different custom subclass, like Tower, etc. I don't see this as healthy programming practice, since methods in one class may not be in the other, yet you are casting them nonetheless.
That may or may not be contributing to your issue, but a wiser approach is to keep an assign iVar CCArray for children of a specific class. i.e. put all your projectiles into one array at the time they are created and added to the batch node then you can iterate through all projectiles, towers, etc by going through those individual arrays, where you know what kind of class they already are. Then remove them from the array as well as from the parent when necessary. I would think this is quite a bit safer than going through all the game's sprites in their entirety that are in your batchnode, which may include spites not being used, perhaps, and casting them and testing them as different classes.

Related

ARC with cocos2d causing unbounded heap growth and eventual memory crash?

I'm trying to track down a memory-related crash in my game. The exact error, if I happen to catch it while attached to a debugger varies. One such error message is:
Message from debugger: Terminated due to memory issue.
No crash report is generated. Here's a screenshot from the XCode7 Memory Report as I play on my iPhone6; after about 10 minutes it will crash, as I enter into the ~600MB+ range:
Running generational analysis with Instruments I've found that playing through battles appears to create unbounded persistent memory growth; here you can see what happens as I play through two battles:
What is confusing is that the allocations revealed by the twirl-down are pretty much every single bit of allocated memory in the whole game. Any read of a string from a dictionary, any allocation of an array, appears in this twirl-down. Here's a representitive example from drilling into an NSArray caller-analysis:
At this point, it occurs to me I started this project using cocos2d-iphone v2.1 a couple of years ago, and I started an ARC project despite using a pre-ARC library. I'm wondering if I'm just now realizing I configured something horribly, horribly wrong. I set the -fno-objc-arc flag on the cocos2d files:
Either that, or I must have done something else very very stupid. But I'm at a loss. I've checked some of the usual culprits, for example:
Put a NSLog in dealloc on my CCScene subclass to make sure scenes were going away
Made sure to implement cleanup (to empty cached CCNodes) and call super in my sublcasses
Dumped the cocos2d texture cache size, and checked it was not growing unbounded
Added low memory warning handlers, doing things like clearing the cocos2d cache
I've also been pouring over the Apple instruments documentation, in particular this link explains the steps I took to create the above generational analysis.
Update
Here's another representative example, this time in tree format. You can see that I have a CCScheduler which called an update function which triggered the UI to draw a sprite. The last time you see my code, before it delves into cocos2d code, is the highlighted function, the code for which I've also pasted below.
+ (instancetype)spriteAssetSource:(NSString*)assetName {
if(!assetName.length) {
return nil;
}
BOOL hasImageSuffix = [assetName hasSuffix:EXT_IMG];
NSString *frameName = hasImageSuffix ? [assetName substringToIndex:assetName.length-EXT_IMG.length] : assetName;
NSString *hdFrameName = [NSString stringWithFormat:#"%#%#",frameName,EXT_HD];
// First, hit up the sprite sheets...
if([[CCSpriteFrameCache sharedSpriteFrameCache] hasSpriteFrameName:hdFrameName]) {
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:hdFrameName];
return [[self alloc] initWithSpriteFrame:frame];
}
// No sprite sheet? Try the assets.
else {
NSString *assetFp = hasImageSuffix ? assetName : [NSString stringWithFormat:#"%#%#",assetName,EXT_IMG];
return [[self alloc] initWithFile:assetFp];
}
}
What's so weird about this is that the captured memory is just the simple line to check if the file name is in the cocos2d cache:
- (BOOL)hasSpriteFrameName:(NSString*)name {
return [_spriteFrames.allKeys containsObject:name];
}
Yet this simple function shows up all over the place in these traces...
What it feels like is that any locally-scoped variable I create and pass into cocos2d gets its retain count incremented, and thus never deallocates (such as the case with both hdFrameName and other variables above).
Update 2
While it's no surprise that the CCScheduler sits at the top of the abandoned objects tree, what is surprising is that some of the objects are completely unrelated to cocos2d or UI. For example, in the highlighted row, all I've done is call a function on AMLocalPlayerData that does a [NSDate date]. The entire line is:
NSTimeInterval now = [NSDate date].timeIntervalSince1970;
It seems absurd that the NSDate object could be retained somehow here, yet that seems to be what Instruments is suggesting...
Update 3
I tried upgrading my version of cocos2d to 2.2, the last version to exist in the repository. The issue persists.

Core Data object with KVO view controller, and gesture-based changes

I have an iPhone/iPad app where one of the main functions is to arrange objects on a plot. I use Core Data to manage the objects' relationships; for this example, I'll talk about Units, which have a to-one relationship to a Plot (and a to-many inverse). Units' properties include positionX, positionY, and angle.
Each Unit (inherits from NSManagedObject) is paired with a UnitViewController (inherits from UIViewController). The Unit has a property .viewController and the UnitViewController has a property .object, so in different uses they can refer to each other. These are set when the Plot is opened or new Units are added (or re-added from Undo, etc).
Each UnitViewController has a UIPanGestureRecognizer for its view, and when that gesture occurs, the UnitViewController changes its .object's positionX and positionY values. When that happens, the UnitViewController then observes those changes through KVO and re-positions the view.
This may seem convoluted, but the reason I did it this way is that I can also change the position numerically in a UITableView. Here's an abbreviated version of the code, showing the crucial bits of the path. Some of these methods exist in my UIViewController+LD category, hence the general names.
- (IBAction)dragObject:(UIPanGestureRecognizer *)gesture
{
// GESTURE BEGAN
if ([gesture state] == UIGestureRecognizerStateBegan) {
[self beginGesture:gesture];
if (!_selected) [self setSelected:YES];
}
// turn on registration before last pass
if ([gesture state] == UIGestureRecognizerStateEnded || [gesture state] == UIGestureRecognizerStateCancelled) {
[[NSNotificationCenter defaultCenter] postNotificationName:ENABLE_UNDO_REGISTRATION object:nil];
}
// MOVE
[self dragUnitWithGesture:gesture];
// turn off registration after first pass
if ([gesture state] == UIGestureRecognizerStateBegan) {
[[NSNotificationCenter defaultCenter] postNotificationName:DISABLE_UNDO_REGISTRATION object:nil];
}
// GESTURE ENDED
if ([gesture state] == UIGestureRecognizerStateEnded ||
[gesture state] == UIGestureRecognizerStateCancelled) {
[self endGesture];
}
}
- (void)dragUnitWithGesture:(UIPanGestureRecognizer *)gesture
{
CGPoint translation = [gesture translationInView:self.view.superview];
[self saveNewObjectCenterWithTranslation:translation];
}
- (void)saveNewObjectCenterWithTranslation:(CGPoint)translation
{
[self saveNewObjectCenter:CGPointMake(initialCenter.x + translation.x, initialCenter.y + translation.y)];
}
- (void)saveNewObjectCenter:(CGPoint)center
{
CGPoint dataPoint = [Converter dataPointFromViewPoint:center];
self.object.positionX = [NSNumber numberWithFloat:dataPoint.x];
self.object.positionY = [NSNumber numberWithFloat:dataPoint.y];
}
- (void)beginGesture:(UIGestureRecognizer *)gesture
{
[[NSNotificationCenter defaultCenter] postNotificationName:BEGIN_UNDO_GROUPING object:nil];
self.initialCenter = self.view.center;
}
- (void)endGesture
{
NSError *error = nil;
[self.object.managedObjectContext save:&error];
[[NSNotificationCenter defaultCenter] postNotificationName:END_UNDO_GROUPING object:nil];
}
My issue comes from crash reports obtained through Crashlytics, because I cannot replicate the crash on my devices. There have been multiple reports that all occur with the stack trace:
_UIGestureRecognizerSendActions
-[UnitViewController dragObject:]
-[UnitViewController dragUnitWithGesture:]
-[UnitViewController saveNewObjectCenterWithTranslation:]
-[UIViewController(LD) saveNewObjectCenter:]
_sharedIMPL_setvfk_core + 110
-[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] + 180
NSKeyValueWillChange + 474
NSKeyValuePushPendingNotificationPerThread + 214
This particular one ended with:
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x00000000
But I've also seen:
Fatal Exception: NSInternalInconsistencyException
An -observeValueForKeyPath:ofObject:change:context: message was received but not handled (keyPath: positionX)
Fatal Exception: NSObjectInaccessibleException
Core Data could not fulfill a fault for ‘0x00000000 <x-coredata://xxxxxx/Unit/p116>'
So my question: is there a known issue with this type of data modification? Is it simply too fast for the Core Data framework to handle? Or is there something else I could be doing wrong? This problem is only one of the ways Core Data issues have manifested in my app, and I'd love to get to the heart of the matter to make my app more stable.
Update:
I don't have enough reputation to post images, but here's a link to the full stack trace:
stackTrace
As much as I agree with Marcus on the spirit of reducing the number of save: calls, I'm not convinced that save: frequency is the source of this particular problem because it's only called when the pan gesture ends.
Instead, the crash-log screenshot looks very much like a KVO observer has been deallocated without first being removed as an observer.
Review your code and make sure that all addObserver:forKeyPath:context: calls are balanced by removeObserver:forKeyPath:context: and that it's not possible for your observer(s) to be deallocated without first removing their observances.
If you can't spot where it might be going wrong then perhaps edit your question to include the KVO-related code so we can take a look at it.
As for the could not fulfill a fault exception, that's a different issue and probably deserves a different stack overflow question, complete with the full stack trace.
Core Data can handle a surprising amount of data at just about any pace you want. However your code may be doing more work than is needed.
First, you appear to be saving on each move. That is completely unnecessary. Core Data works the same whether the entity is saved or not. However, saving to disk is expensive. So if you are saving on every movement/change you can be causing a problem. Removing those saves will only help. Consider saving only when the user expects a pause: leaving the app, changing views, etc.
Second, what thread is this crash coming from? If you have a multi-threaded situation here and you are receiving notifications on the wrong thread you can get some odd crashes. The UI expects/demands that you interact with it only on the main thread (thread 0). Core Data has some rules around threading as well. I cannot tell from your snippet of the stack trace if this is an issue. Posting more of the stack trace will help.
Update
Saving that often hurts the user experience because you are blocking the main thread on each save. While it should not directly cause a crash it could in theory cause notifications to get "queued up" waiting for the writes to be done. Better to save every X seconds or something than block on each notification firing.
Can you post the full stack trace? The snippet you showed has removed a great deal of information that will help to diagnose the problem.
As for not duplicating, that tends to also point towards a threading issue. Not every CPU is identical. A threading issue can show up on a small subset of devices and not duplicatable at all on others. I once had a threading issue show up on a single device.
Update
Further reflection. Wiring up User Events to saving in Core Data is just bad imho. There is no guarantee that the user is going to do anything sane. You would be far better off having the user event kick off a timer. If the timer is already present then kick it out a bit further. When the timer fires, you save.
That will give you a bit of disconnect and protect you from user behavior.
Answered
After a lot of digging and fixing of other bugs, I think I've figured out the problem. I haven't fixed it yet, but I'm able to repeat crashing behavior, so it's only a matter of how many hours it'll take to sort it out.
The problem lies in an ill-conceived undo grouping, which un-does part of an action but not all of it, and when that grouping is undone, a number of different crashes can occur, depending on what action is performed next. Basically, I'm left with a faulted NSManagedObject subclass instance that doesn't exist anymore, and the view controller that reacts to these other actions tries to call it from various places. When that happens, the app obviously crashes, but since none of the actions in the stack trace itself indicate the actual cause of the problem, it was hard to track down.
So this has nothing to do with save frequency, which is good. I'm still curious to see if it prevents the Core Data crashes that I believe were occurring on another thread.
Thanks to everyone who answered!
UPDATE
The actual problem was elsewhere. See my other question here to see the answer and the solution:
make NSUndoManager ignore a property of an NSManagedObject

Why does playing sound/vibrating makes causes a crash?

Im getting location updates from a locationManager with this method:
- (void) locationManager:(CLLocationManager *) manager
didUpdateLocations:(NSArray *)locations
{
for (CLLocation *p in taggedObjectsArray) {
CLLocationDistance distance = [p distanceFromLocation: newLocation];
if(distance < 10.00){
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
// if warning setting is set to sound, play sound
if (beep) {
AudioServicesPlaySystemSound(beep);
}
[taggedObjectsArray removeObject:p];;
}
}
The taggedObjectsArray contains pointers to Cllocation objects
Now it works perfectly as long as there at not two objects in the array at any given run through the for loop. When there is two(or more) the app crashes after playing a single vibration.
Im guessing its because its trying to play a sound/beep on top of another sound/beep, but im not sure.
Anyone have a simple solution to this?
Thanks!
Agree with LuisCien that posting the contents of the debugger from the crash would be helpful.
One (blind) suggestion though would be to hold off on removing each object from taggedObjectsArray during each iteration of the loop and just do it after the fast enumeration completes with a single call to [taggedObjectsArray removeAllObjects];
from a pure efficiency and readability standpoint, it's cleaner to do this once with a stock method designed to remove everything than to invoke a single removal p times, since both end up with the same thing (an empty NSMutableArray)
There's a chance (speculation) that it might solve your crash too, but posting the crash log will help decide that better.

NSDecimalNumberPlaceHolder Leak

I have an iPad app that I am testing in Instruments before beta testing. I have gotten rid of all memory leaks except one, and I can't find any information on it. I am baffled as to what to do, since my code never mentions the leaking object which is an instance of NSDecimalNumberPlaceHolder.
For sure I am using NSDecimalNumber. I create 2 decimals per user operation and each I time I run a cycle of the app (which performs some math operation on the two NSDecimalNumbers) I generate four instances of this NSDecimalPlaceHolder thing. Since I do not know how it gets created, I do not know how to release or dealloc it so as to not generate these 16 btye leaks over and over again.
Is it possible that these are not really leaks?
I have run the XCode Analyzer and it reports no issues.
What I'm doing is this:
I send a decimal number from my controller over to my model (analyzer_) which performs the operations and sends back the result.
[[self analyzer_] setOperand:[NSDecimalNumber decimalNumberWithString:anotherStringValue]];
The setOperand method looks like this:
-(void)setOperand:(NSDecimalNumber*)theOperand
{
NSLog(#"setOperand called");
operand_ = theOperand;
//[operand_ retain];
}
Note that if I don't retain operand_ "somewhere" I get a BAD_ACCESS crash. I currently retain and release it later where the operand and the previously provided operand (queuedOperand_) are operated upon. For example:
{
[self performQueuedOperation];
queuedOperation_ = operation;
queuedOperand_ = operand_;
}
return operand_;
[operand_ release];
where performQueuedOperation is:
-(void)performQueuedOperation
{
[operand_ retain];
if ([#"+" isEqualToString:queuedOperation_])
{
#try
{
operand_ = [queuedOperand_ decimalNumberByAdding:operand_];
}
#catch (NSException *NSDecimalNumberOverFlowException)
{
//viewController will send decimal point error message
}
<etc for the other operations>
}
Let me know if this is not clear. Thanks.
Try Heapshot in Instruments, see: When is a Leak not a Leak?
If there is still a pointer to the memory that is no longer used it is not a leak but it is lost memory. I use Heapshot often, it really works great. Also turn on recording reference counting in the Allocations tool and drill down. Here is a screenshot:

Checking for a collision between multiple dynamic instances in AS3

Okay, here's the rub. I'm making a little maze game. In this maze game, I have walls. They link to a class called Wall.as. In order to collision detect with the player, I do this:
MovieClip(parent).checkCollisonWithPlayer(this);
Where (parent) is a manager logic class. The parent does this
public function checkCollisonWithPlayer(wall:MovieClip)
{
if(player != null)
{
Collision.block(player,wall);
}
}
Now here's my problem. I have a class of Bullet.as that does much the same thing:
private function onEnterFrame(event:Event):void
{
x+= _vx;
//Check for collisions with walls and enemies
MovieClip(parent).checkCollisionWithEnemies(this);
//Remove if it leads off the stage
if (x > stage.stageWidth)
{
parent.removeChild(this);
}
}
What if I wanted to check for a collision between one of these bullets() and one of these walls()? How can I do that? They're never in the same function at the same time and there's almost certainly more than one of each in play at any time. I'm new to AS and this feels like a common issue, but I couldn;t dig up any material that made sense.
Here;s what I need:
public function checkCollisonWithWall(wall:MovieClip,bullet:MovieClip)
{
if(bullet.hitTestObject(wall))
Collision.block(bullet,wall);
}
}
But of course that doesn't work. What are my options?
On a simple level:
1) separate out physics from everything else: it makes things easier and more reusable.
2) have each physics object register itself with, say, a physics manager. things that you need to keep track of are if the object is static or dynamic (moving) and maybe even a shape flag (if you only wanted to collide certain objects with other objects). a shape flag is simply an int that tells you what type it is
3) have one onEnterFrame event, not an enter frame for each object. this will make your life easier
4) on this single onEnterFrame event, call on your physics manager to update the objects and see if any are colliding. You can to check collisions between static and dynamic objects, and (possibly) dynamic objects against other dynamic objects. You can use shape flags to cull out unnecessary tests. If you find a collision, you can dispatch an event/notify your objects/remove them from the game whatever.
something like
private function _init():void
{
// create 4 walls
for( var i:int = 0; i < 4, i++ )
{
var w:Wall = new Wall;
// position and add to stage etc
// add it to the physics manager (they don't need an update)
this._physicsManager.addStatic( w, ShapeFlags.WALL );
}
// create 10 bullets
for( i = 0; i < 10; i++ )
{
var b:Bullet = new Bullet;
// position and add to stage etc
// add it to the update and physics manager
this._updateManager.add( b );
this._physicsManager.addDynamic( b, ShapeFlags.BULLET ); // ShapeFlags is a static class of different types of objects
}
}
private function _onEnterFrame( e:Event ):void
{
// if you're moving your objects based on time, then
// dt is "delta time", i.e., the amount of time that
// has passed since the last update
// you can have an update manager that will move your
// different objects according to their speed.
this._updateManager.update( dt );
// now your objects have moved to their new position,
// get the physics manager (though in fairness, if it's
// only doing collisions, then it should just be a
// collisions manager) to check for collisions
this._physicsManager.update();
// do whatever else
}
This is a very simple example. Here, we're moving objects, then checking for collisions. You could possible run into problems with objects that are moving too fast and passing through each other, so no collision is registered. Something like this:
// frame one: object a is heading towards object b
// and object b is heading towards object a
(a)-> <-(b)
// frame two: because of their speed, they pass through
// each other, and you don't see a collision
<-(b) (a)->
This system is the easiest and simplest way to do this and is probably fine for a lot of games. If you want more complicated (as in checking for collisions between frames), then it's a bit more complicated, but you can still build on this.
Collision detection/response is a big topic, and you can find hundreds of code examples around the net. A good code structure can make the world of difference however.
It looks like the parent manager class has permanent references to the player and the enemies that it uses for collision detection. So also give it references to the walls and bullets.

Resources