Почему мой объект Objective-C освобождается? - PullRequest
6 голосов
/ 25 сентября 2010

У меня проблема с объектом 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 Мне удалось отследить объект (ы), вызывающий проблему, но я до сих пор не могу понять , почему этот объект освобождается , Поиск в моем коде «release» / ​​«dealloc» не дает никаких подсказок.

Я пытался удалить «release» (и даже добавить «retain»!) В цикл alloc / init (см. Вверху), похоже, он продлевает время, в течение которого приложение может работать, но не устраняет проблему полностью.

Любые намеки приветствуются!

EDIT

Благодаря quixoto, Olie, Eiko, 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];)?

Ответы [ 4 ]

2 голосов
/ 25 сентября 2010

Здесь недостаточно кода, чтобы сказать, в чем проблема, но из сообщения об ошибке я бы предположил, что объект playerId - это то, что не сохраняется. То есть, похоже, что с вашим tempCharacter все в порядке, но не поле playerId.

Если у вас есть

@property(nonatomic,retain) SomeObject *playerId;

тогда помните, что

playerId = foo;

будет НЕ удерживать объект за вас. Вы должны использовать аксессор:

self.playerId = foo;

РЕДАКТИРОВАТЬ в ответ на вопрос Тома-edit:

Я абсолютно гарантированно гарантирую, что объекты, помещенные в NSMutableArray, сохраняются этим массивом до тех пор, пока (а) они не будут удалены или (б) массив не будет освобожден. Так что вы можете перестать смотреть туда, проблема в другом. :)

Одна вещь, которую вы можете попробовать, это добавить следующий код к объекту, который выдается, когда вы думаете, что это не следует:

#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 обещает сбалансированный re-release при правильном использовании. Я просто предупреждаю вас, потому что вы, возможно, ожидаете гораздо меньшего количества удержаний, и будете удивлены, увидев, как он поднимается во время той или иной операции.

Удача!

1 голос
/ 25 сентября 2010

Исходя из вашего обновления:

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

Я полагаю, что вы освобождаете существующую ссылку на объект в установщике, а затем сохраняете новую копию.Однако, если новый объект оказывается точно таким же объектом, что и существующая ссылка, возможно, вы отправляете сообщение retain уже освобожденному объекту.

-(void) setSelectedCharacter: (GameCharacter*) newCharacter
{
  [character release]; // Oops if character == newCharacter
  character = [newCharacter retain];
} 
1 голос
/ 25 сентября 2010

Отладка ложных сохранений / выпусков за 3 простых шага:

  1. Переопределение -retain, -release и -autorelease для интересующего вас класса. Заставьте их записать сообщение (NSLog(@"%@ %s", self, sel_getName(_cmd))) и super -call.
  2. Точка останова всех этих методов (на super -call, т.е. после сообщения журнала, чтобы вы знали, какой это объект).Изменить точку останова;добавьте команду «bt» и установите флажок автоматического продолжения (или просто используйте две команды «bt», «продолжить»).
  3. Очистите журнал.Запустите приложение.Распечатайте журнал.Прикрепите это к доске.Нарисуйте несколько стрелок, пока не найдете ложное release / autorelease.

Мое первое впечатление было то, что characterArray был выпущен слишком рано, но это должно привести к жалобам на отправку сообщенияв освобожденный NSArray.Если, конечно, у вас нет доступа к characterArray из нескольких потоков (не делайте этого!).

0 голосов
/ 25 сентября 2010

Вы выпускаете свой экземпляр GameCharacter где-нибудь. Ваш код выглядит нормально, поэтому он где-то в других местах использует эти объекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...