为什么我的Objective-C对象被释放?

为什么我的Objective-C对象被释放?

问题描述:

我有一个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

根据您的更新:

#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

你在某处释放你的GameCharacter实例。你的代码看起来很好,所以它在其他地方使用这些对象的地方。

调试虚假保留/释放3个简单步骤:

  1. 覆盖-retain-release-autorelease你感兴趣的类让他们记录消息(NSLog(@"%@ %s", self, sel_getName(_cmd)))和super -call。
  2. 断点所有这些方法(在super-调用,即日志消息之后,所以你知道它是哪个对象)。编辑断点;添加命令“bt”并检查自动继续框(或仅使用两个命令“bt”,“continue”)。
  3. 清除日志。运行应用程序。打印日志。将它贴在白板上。画一些箭头,直到找到伪造的release/autorelease

我的第一印象是characterArray被释放得太早,但这应该导致它抱怨发送消息到释放的NSArray。当然,除非你从多个线程访问characterArray(不这样做!)。

这里没有足够的代码来告诉问题是什么,但是从错误消息中我猜测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承诺使用均衡的保留发布。我只是警告你,因为你可能期望保留的数量少得多,并且在某些操作或其他操作中看到它爬升会感到惊讶。

好运!