2010-09-24 120 views
6

我有一個Objective-C對象(在iOS遊戲應用程序中)正在被神祕解除分配的問題。爲什麼我的Objective-C對象被釋放?

目的是被實例化,像這樣一個GameCharacter實例:

for (int c = 0; c < kNrOfGuards; c++) { 
    GameCharacter* guard = [[GameCharacter alloc] initGuard:self sprite:guardSprite]; 
    [characterArray addObject:guard]; 
    [guard release]; 
} 

我也有一個方便的方法用於查找一個GameCharacter:

- (GameCharacter*)findCharacterWithIndex:(int)index { 
    return [characterArray objectAtIndex:index]; 
} 

而生成錯誤看起來像代碼:

for (int c = 0; c < [self characterCount]; c++) { 
    GameCharacter* tempCharacter = [self findCharacterWithIndex:c]; 
    if (tempCharacter.playerId == playerIndex]) { 
     ... 
    } 
} 

運行此代碼for som E時間(從未立即)生成在控制檯一個錯誤:

[GameCharacter playerId]:發送到釋放的實例0x4e47560

隨着NSZombieEnabled trick我已經成功消息追查(多個)對象那是造成這個問題的原因,但我仍然不明白爲什麼這個對象正在被釋放。搜索我的代碼「釋放」/「dealloc」不會產生任何線索。

我已經嘗試刪除「釋放」(甚至添加一個「保留」!)到分配/初始化循環(見上),它似乎延長了應用程序可以運行的時間,但並未完全刪除問題。

任何提示將不勝感激!

編輯

感謝quixoto,OLIE,英子,TC,我已經想通了,這是一個正在釋放我GameCharacter對象,但是我還是不太明白爲什麼。下面是跟蹤日誌按時間倒序:

#0 -[GameCharacter dealloc] 
#1 objc_setProperty 
#2 -[TiledGroundLayer setSelectedCharacter:] 
#3 -[TiledGroundLayer selectNextCharacterForPlayer:searchStep:] 
#4 -[GameScene selectNextCharacter:] 
#5 -[GameScene endTurn] 
#6 -[HUDLayer onClickDone:] 

這裏會發生什麼事,是用戶點擊「完成」,在屏幕上選擇的字符改變,因此財產selectedCharacter上TiledGroundLayer(步驟#2-4 )。由於selectedCharacter擁有以前的GameCharacter對象,因此它似乎正在被釋放。但爲什麼它沒有被NSMutableArray([characterArray addObject:guard];)正確保留?

+3

您的characterArray變量創建爲'[NSMutableArray array]'還是'[[NSMutableArray init] alloc]'? – tidwall 2010-09-24 22:11:55

+0

@Tom - 這些代碼片段在哪些類中生存?它看起來像其他地方的代碼與導致dealloc的characterArray交互。可能與從數組中刪除對象相關 - 例如 - 是否發生? – 2010-09-24 22:14:27

+0

我會仔細看看characterArray以及何時釋放它。既然你有一個方便的函數來返回一個守衛,你可以在characterArray及其所有內容被釋放後輕鬆地保留它。如果你沒有方便的方法(我不能在這裏看到它的好處),你可以在需要的時候從數組中獲得警衛,並且更確定characterArray在那時仍然是必需的和有效的。 – 2010-09-24 23:03:07

回答

1

根據您的更新:

#0 -[GameCharacter dealloc] 
#1 objc_setProperty 
#2 -[TiledGroundLayer setSelectedCharacter:] 

我猜你釋放你現有的對象引用您的二傳手,其次是保留新副本。但是,如果新對象恰好與現有引用完全相同,則可能會將retain消息發送到已釋放的對象。

-(void) setSelectedCharacter: (GameCharacter*) newCharacter 
{ 
    [character release]; // Oops if character == newCharacter 
    character = [newCharacter retain]; 
} 
+0

該屬性被定義爲:'@property(nonatomic,retain)GameCharacter * selectedCharacter;' - 是不是「保留」部分照顧這個? – 2010-09-25 09:14:45

+0

保羅亞歷山大,你絕對是對的!它是過早釋放對象的@property(nonatomic,** retain **)的「保留」部分。通過將其更改爲「分配」,問題就解決了。感謝大家! – 2010-09-25 16:38:38

+1

只需要清楚,將其更改爲分配(來自保留)會更改代碼的行爲。你可能已經修復了這個錯誤,但要小心你不只是介紹其他十幾個人。 – Olie 2010-09-25 22:28:17

0

你在某處釋放你的GameCharacter實例。你的代碼看起來很好,所以它在其他地方使用這些對象的地方。

1

調試虛假保留/釋放3個簡單步驟:

  1. 覆蓋-retain-release-autorelease你感興趣的類讓他們記錄消息(NSLog(@"%@ %s", self, sel_getName(_cmd)))和super -call。
  2. 斷點所有這些方法(在super-調用,即日誌消息之後,所以你知道它是哪個對象)。編輯斷點;添加命令「bt」並檢查自動繼續框(或僅使用兩個命令「bt」,「continue」)。
  3. 清除日誌。運行應用程序。打印日誌。將它貼在白板上。畫一些箭頭,直到找到僞造的release/autorelease

我的第一印象是characterArray被釋放得太早,但這應該導致它抱怨發送消息到釋放的NSArray。當然,除非你從多個線程訪問characterArray(不這樣做!)。

2

這裏沒有足夠的代碼來告訴問題是什麼,但是從錯誤消息中我猜測playerId對象是沒有被保留的。也就是說,你的tempCharacter似乎很好,但不是playerId字段。

如果你有

@property(nonatomic,retain) SomeObject *playerId; 

話,記得

playerId = foo; 

守住你的對象。您必須使用訪問:

self.playerId = foo; 

編輯響應湯姆的問題編輯:

我絕對,正面向你保證,放在一個NSMutableArray對象是由陣列中,直至留存(一)他們被刪除或(b)陣列被釋放。所以你可以停止看那裏,問題在別的地方。 :)

有一兩件事你可以嘗試是將以下代碼添加到當你認爲它不應該是被釋放的對象:

#pragma mark - 
#pragma mark Memory-use debugging 

#define DEBUG_RETAIN_RELEASE 0 
#define DEBUG_ALLOC_DEALLOC  0 



#if DEBUG_ALLOC_DEALLOC 

static int allocCounter = 0; 
+(id)alloc 
{ 
    id me = [super alloc]; 
    NSLog(@"%@ ALLOC (%2d): %@", [me class], ++allocCounter, me); 

    return me; 
} 

#endif 


#if DEBUG_RETAIN_RELEASE 
- (id)retain 
{ 
    id result = [super retain]; 
    NSLog(@"%@ retain  %@, count: %2d", [self class], self, [self retainCount]); 
    return result; 
} 


- (void)release 
{ 
    // we have to log BEFORE the release, in case it's the last one! e 
    NSLog(@"%@ RELEASE  %@, count: %2d", [self class], self, ([self retainCount] - 1)); 
    [super release]; 
} 


- (id)autorelease 
{ 
    id result = [super autorelease]; 
    NSLog(@"%@ AUTOrelease %@, count: %2d", [self class], self, [self retainCount]); 
    return result; 
} 

#endif 

// ... 


- (void)dealloc 
{ 
#if DEBUG_ALLOC_DEALLOC 
    NSLog(@"%@ dealloc (%2d): %@", [self class], --allocCounter, self); 
#endif 

    [self releaseMyStuff]; 
    [super dealloc]; 
} 

然後用DEBUG_ALLOC_DEALLOC = 1開始,把一個斷點dealloc日誌語句。如果這沒有幫助,請將DEBUG_RETAIN_RELEASE設置爲1,然後分別保留&版本。

如果使用得當,您會對所有iOS保留感到驚訝,但不用擔心,iOS承諾使用均衡的保留髮布。我只是警告你,因爲你可能期望保留的數量少得多,並且在某些操作或其他操作中看到它爬升會感到驚訝。

好運!

相關問題