【问题标题】:Why is my Objective-C object being deallocated?为什么我的 Objective-C 对象被释放?
【发布时间】:2011-04-17 00:27:28
【问题描述】:

我遇到了一个 Objective-C 对象(在 iOS 游戏应用程序中)被神秘地释放的问题。

该对象是一个 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]) {
        ...
    }
}

运行此代码一段时间(从不立即)在控制台中生成错误:

[GameCharacter playerId]:消息发送到已释放实例 0x4e47560

通过NSZombieEnabled trick,我设法找到了导致问题的对象,但我仍然无法理解为什么这个对象被释放。在我的代码中搜索“release”/“dealloc”不会产生任何线索。

我尝试将“释放”(甚至添加“保留”!)到分配/初始化循环(见顶部),它似乎延长了应用程序可以运行的时间,但不能完全消除问题。

任何提示将不胜感激!

编辑

感谢 quixoto、Olie、Eiko 等,我发现是我的 GameCharacter 对象正在被释放,但我仍然不明白为什么。以下是按时间倒序排列的跟踪日志:

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

这里发生的情况是,用户单击“完成”,屏幕上选定的字符被更改,因此 TiledGroundLayer 上的属性selectedCharacter (步骤#2-4)。由于selectedCharacter 拥有之前的 GameCharacter 对象,它似乎正在被释放。但是为什么 NSMutableArray ([characterArray addObject:guard];) 没有正确保留它?

【问题讨论】:

  • 您的 characterArray 变量是否创建为 [NSMutableArray array][[NSMutableArray init] alloc]
  • @Tom - 这些代码 sn-ps 存在于哪些类中?看起来您在其他地方有与导致 dealloc 的 characterArray 交互的代码。例如,可能与从数组中删除对象相关联 - 会发生这种情况吗?
  • 我会仔细查看 characterArray 以及它何时被释放。因为你有一个方便的函数来返回一个守卫,你可以很容易地在 characterArray 之后持有它,并且它的所有内容都被释放。如果您没有方便的方法(并且我在这里看不到它的好处),您可以在需要时从数组中获取防护,并更加确定此时 characterArray 仍然是必需的并且有效。
  • GameCharacter 中的dealloc 方法的顶部添加一个断点。无论如何,你会看到谁拉了最后一个版本。
  • @jojaba:它被创建为characterArray = [[NSMutableArray alloc] init];

标签: iphone objective-c memory


【解决方案1】:

这里没有足够的代码来说明问题所在,但是,从错误消息中,我猜 playerId 对象是没有被保留的对象。也就是说,您的 tempCharacter 似乎很好,但 playerId 字段却不行。

如果你有

@property(nonatomic,retain) SomeObject *playerId;

然后记住

playerId = foo;

为您抓住对象。您必须使用访问器:

self.playerId = foo;

编辑回应汤姆的问题-编辑:

我绝对肯定地向您保证,放置在 NSMutableArray 中的对象将由该数组保留,直到 (a) 它们被删除或 (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 承诺平衡的保留释放。我只是警告您,因为您可能期望保留计数要低得多,并且惊讶地看到它在某些操作或其他操作中攀升。

好运!

【讨论】:

    【解决方案2】:

    根据您的更新:

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

    我猜您是在释放对 setter 中对象的现有引用,然后保留新副本。但是,如果新对象恰好与现有引用完全相同,您可能会将retain 消息发送到已释放的对象。

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

    【讨论】:

    • 属性定义为:@property (nonatomic, retain) GameCharacter* selectedCharacter; - “保留”部分不会处理这个问题吗?
    • 保罗亚历山大,你说得对!是@property (nonatomic, **retain**) 的“保留”部分过早地释放了该对象。通过将其更改为“分配”,问题就解决了。谢谢大家!
    • 为了清楚起见,将其更改为分配(从保留)会更改代码的行为。您可能已经修复了这个错误,但请注意您没有只介绍十几个其他错误。
    • Paul 试图描述的是:在他的示例代码中,假设 character 和 newCharacter 是同一个对象。该函数的第一行释放 NEWCHARACTER,然后第二行尝试保留一个保留对象。执行该功能的正确顺序是: id oldCharacter = character;字符 = [新字符保留]; [oldCharacter release];看到了吗?
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-20
        • 2010-12-22
        • 2011-06-16
        • 2011-11-09
        相关资源
        最近更新 更多