2011-11-08 67 views
0

我有一个街机风格的iPhone游戏,它是用Cocos2d-iPhone构建的,并且工作正常,但它在无法预测的播放量之后没有明显的原因随机崩溃。有时你可以在没有问题的情况下玩几个级别,而其他的玩家在几秒钟后就会崩溃。在XCode中有一些常见的行通常是EXC_BAD_ACCESS,它会提示一个内存事物,但是这些对象都是自动释放的,并且有额外的保留,并且崩溃非常不一致。似乎适合的一件事是,随着越来越多的事情发生崩溃的可能性增加,这再次表明一些内存问题,但是没有应用程序内存警告被解雇。cocos2d随机崩溃的可能原因是什么?我如何追踪它们?

一些示例崩溃线路有:

if(gameGlobals.gameState == GAME_STATE_PAUSED) {...} 
if(![hearts isKindOfClass:[CCSprite class]]) {...} 

但这两行有一个是fune了好半天,直到崩溃全局对象。

所以,我的问题是,我该如何去追踪这个问题,是否有可能的罪魁祸首?

谢谢。

UPDATE

我一直在做一点侦探工作,我至少有一定的一致性。我认为有两件事情正在进行:

1)是,用于检测与像素数据删除的对象像素完美冲突的数学正在烹饪CPU。

2)我有一些僵尸精灵,否则,正在释放出我的意识。

当我添加一颗子弹雪碧我用:

CCSprite *bullet = [CCSprite spriteWithFile:@"bullet.png"]; 
bullet.tag = BULLET_TAG_GOODY; 
bullet.position = ccp(player.position.x,50); 
[self addChild:bullet]; 

[bullet runAction:[CCSequence actions: 
[CCMoveTo actionWithDuration:2 position:ccp(player.position.x,320)], 
            [CCCallFuncN actionWithTarget:self selector:@selector(bulletMoveFinished:)], 
            nil]]; 
[bullets addObject:bullet]; 

然后我遍历数组子弹。目前它崩溃说我正试图访问一个释放的实例。

所以,1)如何设置这个,所以我的精灵不会被释放?从CCSprite中删除autorelease是个好主意吗? 2)探测子弹与太空入侵者风格基地的最佳方式是什么?

目前:

if([self overlapBases: bullet.position.x :bullet.position.y :true]) {...} 

-(bool)overlapBases :(GLfloat)x :(GLfloat)y :(bool)up { 
// NSLog(@"overlapping - %f,%f",x,y); 
if(y > 20 && y < 100) { 
    int bn = -1; 
    if(x > 28 && x < 118) { 
     bn = 0; 
    } 

    if(x>140 && x<230) { 
     bn = 1; 
    } 

    if(x>254 && x<343) { 
     bn = 2; 
    } 

    if(x>365 && x<453) { 
     bn = 3; 
    } 

    if(bn> -1) { 
    NSLog(@"overlapping - %f,%f",x,y); 
//  for (int ix = 0; ix < NUM_BASES; ix++) { 

     if (overLap(x, 2, aspeaker[bn].position.x, BASE_WIDTH * aspeaker[bn].scale)) { 
      if (overLap(y, 4, aspeaker[bn].position.y, BASE_HEIGHT * aspeaker[bn].scale)) { 
       NSLog(@"ix: %i, x: %f, y: %f",bn,x,y); 
       return [self fineCollision:bn :x :y :up]; 
      } 
     } 
    } 
} 
return false;  
} 


-(bool)fineCollision :(GLuint)baseNum :(GLfloat)x :(GLfloat)y :(bool)up { 
//convert bullet x and y passed in to this base coords 
//check if solid 
//if so, remove bits in gfx and array, return true 
//else return false; 

GLfloat bullx = (x - aspeaker[baseNum].position.x + BASE_WIDTH)/2.0; 
GLfloat bully = (y - aspeaker[baseNum].position.y + BASE_HEIGHT)/2.0; 
GLint testx = (GLint)bullx; 
if ((testx < 0) | (testx >= BASE_WIDTH)) { 
return false; 
} 

GLuint testy; 
bool hit = false; 

for (int iy = -2; iy < 2; iy++) { 

testy = (GLint)(bully - iy);        
if ((testy >= 0) & (testy < BASE_HEIGHT)) { 

if (baseShape[baseNum][15 - testy][testx] > 0) { 


if(showrm == YES) { 
    CCSprite *maskSprite = [CCSprite spriteWithFile:@"bullet-mask.png"]; 

    [maskSprite setBlendFunc: (ccBlendFunc) {GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}]; 

    maskSprite.position = ccp(x , y-(40+22-5)); 

    NSLog(@"sprite: %f",maskSprite.position.x); 

    NSLog(@"render mask: %f",rm.frameInterval); 
    [rm addSpriteMask:maskSprite]; 





    [rm drawCurrent]; 


} 


[self remove:testx :testy :baseNum :up]; 

GLuint seed = rand()%64; 
if (seed & 1) { 

[self remove:testx - 1:testy - 1 :baseNum :up]; 
} 
if (seed & 2) { 
[self remove:testx - 1:testy :baseNum :up]; 
} 
if (seed & 4) { 
[self remove:testx - 1:testy + 1:baseNum :up]; 
} 
if (seed & 8) { 
[self remove:testx + 1:testy - 1:baseNum :up]; 
} 
if (seed & 16) { 
[self remove:testx + 1:testy :baseNum :up]; 
}    
if (seed & 32) { 
[self remove:testx + 1:testy + 1:baseNum :up];     
} 

hit = true;     
} 
} 
} 

return hit; 
} 



- (void)remove:(GLint)offX :(GLint)offY :(GLuint)baseNum :(bool)up { 
if ((offX < 0) | (offX >= BASE_WIDTH)) { 
return; 
} 

if ((offY < 0) | (offY >= BASE_HEIGHT)) { 
return; 
} 
baseShape[baseNum][15 - offY][offX] = 0; 


} 

bool overLap(GLfloat x1, GLfloat w1, GLfloat x2, GLfloat w2) { 

GLfloat left1, left2; 
GLfloat right1, right2; 

GLfloat halfWidth1 = w1 * 0.5f; 
GLfloat halfWidth2 = w2 * 0.5f; 

left1 = x1 - halfWidth1; 
left2 = x2 - halfWidth2; 
right1 = x1 + halfWidth1; 
right2 = x2 + halfWidth2; 

if (left1 > right2) { 
    return false; 
} 

if (right1 < left2) { 
    return false; 
} 

return true; 
} 

有关检测和rendermask想到用大量的精灵和磨损的混合模式。

谢谢。

更新再次:)

好的,我发现了一些僵尸!

Address Category Event Type RefCt Timestamp Size Responsible Library Responsible Caller 

0 0x13a00e90 CCSprite Malloc 1 00:32.974.212 432 SpacedInvaders +[CCSprite spriteWithFile:] 

1 0x13a00e90 CCSprite Autorelease <null> 00:32.974.235 0 SpacedInvaders +[CCSprite spriteWithFile:] 

2 0x13a00e90 CCSprite Retain 2 00:32.974.546 0 SpacedInvaders -[CCArray insertObject:atIndex:] 

3 0x13a00e90 CCSprite Retain 3 00:32.974.629 0 SpacedInvaders -[CCActionManager addAction:target:paused:] 

4 0x13a00e90 CCSprite Retain 4 00:32.974.634 0 SpacedInvaders -[CCArray addObject:] 

5 0x13a00e90 CCSprite Release 3 00:32.986.279 0 QuartzCore CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long) 

6 0x13a00e90 CCSprite Release 2 00:33.074.889 0 SpacedInvaders -[CCArray removeObject:] 

7 0x13a00e90 CCSprite Release 1 00:33.074.915 0 SpacedInvaders -[CCActionManager deleteHashElement:] 

8 0x13a00e90 CCSprite Release 0 00:33.074.918 0 SpacedInvaders -[CCArray removeObject:] 

9 0x13a00e90 CCSprite Zombie -1 00:33.074.939 0 SpacedInvaders -[GameLayer update:] 

那么这里发生了什么?我发现保留数量过低,但我如何避免这种情况。我想我正在做一些愚蠢的顺便说一句。

谢谢。

+0

好,感谢到目前为止评论。 – Martin

+0

OK,感谢评论到目前为止(再次)。要明确autoreleases是cocos2d核心代码的一部分。僵尸已启用,但没有任何工具。没有明显的泄漏或其他(也可以看到)。 – Martin

+1

僵尸是不是乐器,而不是他们改变EXC_BAD_ACCESS撞到上面写着“XXXX送到YYYY的释放实例”,帮助您找出问题的消息。也许这调试速成班可能帮助:http://www.learn-cocos2d.com/2011/10/xcode-4-debugging-crashcourse/ – LearnCocos2D

回答

0

这听起来像记忆问题。如果这是一个巨大的内存泄漏,你以前也不一定会得到内存警告。

你尝试剖析你的游戏与XCode的泄漏?如果是内存问题,您将在分析过程中崩溃时看到它。

0

“对象都是自动释放”听起来像是可能的原因。 Autorelease在UI“空出来”(从当前的UI事件处理逻辑返回控制权)时生效,并且如果您有一个自动发布的变量,您希望在此之后继续使用它,那么它很可能已经“poof” (尽管经常可以在一段时间后使用它,直到存储被重用)。

您是否运行过分析仪或其他工具?

0

如果我理解你正确地再“自动释放额外保留”听起来像一个问题(除非你也有多余的版本),这可能会导致泄漏,导致内存使用增加而使其它对象被释放,你再随后尝试访问。

尝试启用nszombies:How to enable NSZombie in Xcode?

也使用的NSLog语句来跟踪时,当你希望他们没有对象为空。

,如果你知道一些,如果你在哪里得到崩溃的情况,那么也许你可以在此之前,将断点在空校验的东西:

if(gameGlobals.gameState != NULL) { 
    if(gameGlobals.gameState == GAME_STATE_PAUSED) {...} 
} else { 
    //break or nslog here 
} 
1

的唯一方式这条线可能会崩溃

if(gameGlobals.gameState == GAME_STATE_PAUSED) {...} 

是如果gameGlobals是已经被解除分配的一个Objective-C的对象。

如果gameGlobals是C struct,您不会看到EXC_BAD_ACCESS,因为它不是指针。

如果gameGlobalsnil对象,您将看不到EXC_BAD_ACCESS,因为发送消息到nil是可以的。

你的声明“的对象是有额外的所有自动释放保留”环警钟我,因为它表明你真的不明白的内存管理规则。应该在应用的整个生命周期中生存的全局对象不需要被自动释放。您应该在开始时进行分配,就是这样。另外,考虑让你的应用程序委托的全局对象属性。

如果您仍然无法弄清楚,阿德的回答第一部分是去(NSZombie)的方式,虽然第二部分可能不会帮助。我怀疑你的对象是否成为零。

+0

GameGlobals是一个目标C类。它通过一个简单的alloc-init实例化为一个全局实例。此对象不会自动释放,只有cocos2d的对象是这是默认的情况。 – Martin

+0

@马丁:你能告诉你的宣言,定义和它,请初始化。 – JeremyP

0

首先要认识到的是可能没有什么不对,EXC_BAD_ACCESS出现的代码是没有问题的。但是,为了以防万一,值得研究这个特定对象。追求的第一个途径是其中一个目标已被释放,因为它没有被充分保留。所以,是的,根据其他人的建议,在这里肯定会启用僵尸。被释放

对象可能不会被立即释放。这就是为什么EXC_BAD_ACCESS可能发生在奇怪的时代。只有当它们使用的内存块不再需要时才会释放这些对象。其他对象当然会使用相同的块。所以,当这个块有资格取消分配时,就像开发人员一样无法控制。这由运行时处理。

最好的办法是先运行分析器,然后运行仪器与仪器泄漏剖析。

有这一切对娄佛朗哥的网站跟踪EXC_BAD_ACCESS错误很好的解释,并逐点建议:

Understanding EXC_BAD_ACCESS

相关问题