2010-09-24 3 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]) { 
     ... 
    } 
} 

Выполнение данного запроса для сом е время (никогда не сразу) генерирует ошибку в консоли:

[GameCharacter playerId]: сообщение, отправленное высвобождены например 0x4e47560

С NSZombieEnabled trick мне удалось отследить объект (ы) что вызывает проблему, но я до сих пор не могу понять , почему этот объект освобождается. Поиск моего кода для «release»/​​«dealloc» не дает никаких подсказок.

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

Любые советы были бы высоко оценены!

EDIT

Благодаря quixoto, Оли, Эйко, дц., Я понял, что это мой 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

+0

@Tom - в каких классах эти фрагменты кода живут? Похоже, у вас есть код в другом месте, который взаимодействует с characterArray, что приводит к dealloc. Может быть связано с удалением объекта из массива, например, это происходит? –

+0

Я бы внимательно посмотрел на characterArray и когда он будет освобожден. Так как у вас есть функция удобства, чтобы вернуть защитник, вы можете легко удерживать его после characterArray и все его содержимое освобождается.Если у вас не было удобного метода (и я не вижу его преимущества здесь), вы можете вместо этого просто взять защитник из массива всякий раз, когда вам это нужно, и быть более уверенным в том, что characterArray по-прежнему требуется и действителен в этот момент. –

ответ

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, keep) GameCharacter * selectedCharacter;' - не будет ли эта часть «сохранить»? –

+0

Пол Александр, ты совершенно прав! Это была «сохраняемая» часть '@property (nonatomic, ** keep **)', которая преждевременно освобождала объект. Изменяя его вместо «назначать», проблема решена. Всем спасибо! –

+1

Просто, чтобы быть ясным, изменение его для назначения (из сохранения) изменяет поведение кода. Возможно, вы исправили эту ошибку, но будьте осторожны, чтобы вы не просто представили еще десяток других. – Olie

0

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

1

Debugging поддельной сохраняет/выбросы в 3 простых шага:.

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

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

2

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

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

@property(nonatomic,retain) SomeObject *playerId; 

то помните, что

playerId = foo; 

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

self.playerId = foo; 

EDIT в ответ на Тома вопрос-редактирование:

Я абсолютно положительно гарантировать, что объекты, помещенные в 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 log. Если это не поможет, установите DEBUG_RETAIN_RELEASE = 1 и разблокируйте оба варианта: сохранить &.

Вы будете удивлены тем, что iOS сохранит вас, но не беспокойтесь об этом, iOS обещает сбалансированное сохранение-освобождение при правильном использовании. Я просто предупреждаю вас, потому что вы, возможно, ожидаете гораздо меньшего количества удержаний и будете удивлены, увидев, что он поднимается во время какой-то операции.

Удача!

Смежные вопросы