【发布时间】:2011-11-08 11:07:33
【问题描述】:
我有一个街机风格的 iPhone 游戏,它是用 Cocos2d-iPhone 构建的,运行良好,只是在玩了不可预知的游戏后,它会无缘无故地随机崩溃。有时您可以毫无问题地玩多个关卡,而其他关卡会在几秒钟后崩溃。 XCode 中报告了一些常见的行,通常是 EXC_BAD_ACCESS ,这表明内存问题,但对象都是带有额外保留的自动释放,并且崩溃非常不一致。似乎合适的一件事是,随着更多事件的发生,崩溃的可能性增加,这再次表明存在一些内存问题,但随后没有发出应用程序内存警告。
一些示例崩溃行是:
if(gameGlobals.gameState == GAME_STATE_PAUSED) {...}
if(![hearts isKindOfClass:[CCSprite class]]) {...}
但是这两行都有全局对象,这些对象在崩溃之前很长一段时间内都很有趣。
所以,我的问题实际上是我该如何追查问题,是否有任何可能的罪魁祸首?
谢谢。
更新
我一直在做一些侦探工作,我至少有一些一致性。我认为发生了两件事:
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 中删除自动释放是个好主意吗? 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;
}
对于检测和渲染蒙版,请考虑使用大量精灵及其混合模式来进行消耗。
谢谢。
再次更新:)
好的,我找到了一些僵尸!
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:]
那么这里发生了什么?我看到保留计数太低了,但我该如何避免这种情况。我假设我在做一些愚蠢的事情。
谢谢。
【问题讨论】:
-
好的,到目前为止感谢 cmets。
-
好的,感谢 cmets 到目前为止(再次)。需要明确的是,自动发布是 cocos2d 核心代码的一部分。僵尸已启用,但仪器中没有。没有明显的泄漏或其他(我可以看到)。
-
僵尸不是为了工具,而是将 EXC_BAD_ACCESS 崩溃更改为“xxxx 发送到已释放的 yyyy 实例”的消息,帮助您查明问题。也许这个调试速成课程可能会有所帮助:learn-cocos2d.com/2011/10/xcode-4-debugging-crashcourse
标签: iphone objective-c cocos2d-iphone