EXC_BAD_ACCESS на iPhone при использовании «obj! = Nil» - PullRequest
1 голос
/ 31 августа 2009

У меня есть очень простая строка кода в Objective-C:

if ((selectedEntity != nil) && [selectedEntity isKindOfClass:[MobileEntity class]])

Время от времени и без причины я могу сказать, что игра вылетает на этой строке кода с EXC-BAD-ACCESS. Обычно кажется, что это время, когда что-то удаляется с игрового поля, поэтому я предполагаю, что то, что было , selectedEntity получает dealloc'd, тогда это приводит к результату. Помимо невозможности выбрать выходящие сущности (но кто знает, может быть, это на самом деле не так в моем коде ...), тот факт, что я специально проверяю, есть ли selectedEntity, прежде чем я доступ это означает, что у меня не должно быть никаких проблем здесь. Предполагается, что Objective-C поддерживает логическое короткое замыкание, но, похоже, это не РЕДАКТИРОВАТЬ: похоже, что короткое замыкание не имеет никакого отношения к проблеме.

Кроме того, я поместил @ try / @ catch вокруг этого блока кода, потому что я знал, что он время от времени взрывается, но это, кажется, игнорируется (я предполагаю, что EXC-BAD-ACCESS не может быть перехвачен ).

Так что, в общем, мне интересно, знает ли кто-нибудь, как я могу поймать это и выбросить (потому что я не забочусь об этой ошибке, пока она не приводит к падению игры), или объяснить, почему она может происходить. Я знаю, что Objective-C делает странные вещи со значением «nil», поэтому я предполагаю, что он указывает на какое-то странное пространство, которое не является ни указателем объекта, ни nil.

РЕДАКТИРОВАТЬ: Просто чтобы уточнить, я знаю, что приведенный ниже код является неправильным, это то, что я предполагал, происходило в моей программе. Я спрашивал, не вызовет ли это проблемы, а это действительно так. : -)

РЕДАКТИРОВАТЬ: Похоже, что есть дополнительный случай, который позволяет вам выбрать сущность, прежде чем она будет стерта. Итак, похоже, что прогресс кода выглядит так:

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

Так что я предполагаю, что, поскольку пул Autorelease еще не был освобожден, объект не равен nil, но его счетчик хранения равен 0, поэтому доступ к нему все равно запрещен ... или что-то в этом роде?

Кроме того, моя игра однопоточная, так что это не проблема с потоками.

РЕДАКТИРОВАТЬ: я исправил проблему двумя способами. Во-первых, я не позволил выбрать сущность в этом крайнем случае. Во-вторых, вместо того, чтобы просто вызывать [entity removeObjectAtIndex: i] (код для удаления любых сущностей, которые будут удалены), я изменил его на:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

Просто убедитесь, что вы присваиваете nil переменной в то же самое время, когда вы ее отпускаете, как предложено в кливере.

Ответы [ 5 ]

9 голосов
/ 31 августа 2009

Это не имеет никакого отношения к короткому замыканию. Objective-C ест сообщения в ноль, поэтому проверка для selectedEntity != nil не требуется (так как messages-to-nil вернет NO для типов возврата BOOL).

EXC_BAD_ACCESS не является отслеживаемым исключением. Это катастрофический сбой, обычно вызванный попыткой проследить неверный указатель.

Скорее всего, какой бы объект selectedEntity ни указывал, он был освобожден до выполнения кода. Таким образом, это ни ноль, ни действительный объект.

Включите NSZombies и попробуйте снова.

Если ваше приложение является многопоточным, правильно ли вы синхронизируете selectedEntity между потоками (имея в виду, что в целом разделение пользовательского интерфейса из вторичных потоков не поддерживается)?


Ваше сообщение было отредактировано, чтобы указать, что исправление:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

Это устраняет проблему, потому что NSMutableArray будет освобождать объекты при удалении. Если количество сохранений падает до нуля, объект освобождается, а selectedEntity будет указывать на освобожденный объект.

6 голосов
/ 01 сентября 2009

если объект (selectedEntity) был освобожден и освобожден, это не == ноль. Это указатель на произвольный фрагмент памяти, и определение его (если (selectedEntity! = Nil) является ошибкой программирования (EXC_BAD_ACCESS).

Отсюда и общая парадигма obj-c: -

[selectedEntity release]; selectedEntity = nil;

0 голосов
/ 01 сентября 2009
 

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

 

У вас есть свисающий указатель или зомби. SelectedEntity указывает на объект, который получает выпуски непосредственно перед тем, как вы ссылаетесь на selectedEntity. Это делает selectedEntity ненулевым, но недопустимым объектом, поэтому любая разыменование его приведет к сбою.

Вы хотели автоматически выпустить объект, а не выпускать его.

0 голосов
/ 31 августа 2009

Поместите точку останова в OBJC_EXCEPTION_THROW и посмотрите, где она действительно выбрасывается. Эта строка никогда не должна выдавать EXC_BAD_ACCESS сама по себе.

Возможно, вы делаете что-то в блоке IF, что может вызвать исключение?

0 голосов
/ 31 августа 2009

Я только что прочитал http://developer.apple.com/mac/library/qa/qa2004/qa1367.html, что указывало на то, что ошибка, которую вы получаете, является результатом чрезмерного освобождения объекта. это означает, что хотя selectedEntity имеет значение nill, вы выпускали его много раз, и его больше нельзя использовать ..

...