2011-11-08 63 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

相關問題