Метод Dealloc в iOS и установка объектов на nil

У меня довольно простой вопрос. В некоторых примерах, которые я видел, объекты только что выпущены в методе dealloc. В других случаях объекты освобождаются, а затем устанавливаются на nil. Для этого есть причина? Установлено ли значение nil после отпускания выгодного?

Ответ 1

Три способа dealloc

1. Просто отпустите

- (void)dealloc {
    [airplane release];
    [super dealloc];
}

Теперь ссылка на объект указывает на случайную позицию, которая может быть одной из двух вещей:

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

Эффект дальнейшего вызова метода через этот указатель - один из этих трех (один из них - undefined):

  • Авария с EXC_BAD_ACCESS, потому что указатель указывает на мусор.
  • Сбой с селектором undefined, поскольку он указывает на допустимый объект, который не имеет этого метода.
  • Успешное выполнение метода, поскольку новый объект имеет метод с тем же именем.

2. Release и nil

- (void)dealloc {
    [airplane release], airplane = nil;
    [super dealloc];
}

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

3. Nil и релиз

- (void)dealloc {
    id temp = airplane;
    airplane = nil;
    [temp release];
    [super dealloc];
}

Это то же самое, что и раньше, но оно удаляет это маленькое окно между release и nil, где ссылка на объект указывает на недопустимый объект.

Какой из них лучше?

Это вопрос выбора:

  • Если вы скорее сработали, выберите только выпуск.
  • Если вы скорее проигнорируете ошибку, выберите nil + release или релиз + nil.
  • Если вы используете NSZombieEnabled=TRUE, тогда просто отпустите, не нуль зомби!

Макросы и зомби

Простым способом отсрочки выбора является использование макроса. Вместо [airplane release] вы пишете safeRelease(x), где safeRelease - это следующий макрос, который вы добавляете в целевой файл .pch:

#ifdef DEBUG
  #define safeRelease(x) [x release]
#else
  #define safeRelease(x) [x release], x=nil
#endif

Этот макрос не уважает зомби. Вот проблема: когда NSZombieEnabled есть TRUE, объект превращается в NSZombie. Если вы потеряете ссылку на объект, любой вызов, отправленный ему, будет проигнорирован.

Чтобы исправить это, вот макрос из Кевин Баллард, который устанавливает указатель на недопустимую ссылку, ТОЛЬКО, когда NSZombieEnabled - FALSE. Это гарантирует сбой во время отладки, если зомби не включены, но в противном случае останутся зомби.

#if DEBUG
  #define safeRelease(x) do { [x release]; if (!getenv("NSZombieEnabled")) x = (id)0xDEADBEEF; } while (0)
#else
  #define safeRelease(x) [x release], x = nil
#endif

Ссылки

Apple не имеет рекомендации, по которой лучше. Если вы хотите прочитать мысли сообщества, здесь есть некоторые ссылки (темы комментариев тоже замечательные):

Ответ 2

Этот фрагмент охватывает все базы и готов к вырезанию и вставке в файл .pch.

// SAFE_RELEASE
//      Releases an object, then does other things based on context.
//
//      The intention is to fail early during internal testing but prevent
//          customers from experiencing crashes if at all possible.
//
// For more information see:
//      http://stackoverflow.com/questions/6778793/dealloc-method-in-ios-and-setting-objects-to-nil
//
// Debug build:
//      If zombies are enabled, the macro just calls |release|. The zombie
//          mechanism will continue to be used to find messages sent to
//          the deallocated object.
//      Otherwise, zombies are not enabled, so the macro sets the object to a
//          invalid memory address. (0xDEADBEEF.) This will intentionally
//          cause a crash if the object is used, allowing the bug to be found
//          and fixed immediately.
//
// Release build:
//      The macro calls |release| normally. Then it sets the object to nil to
//          prevent a possible crash caused by sending a message to a
//          deallocated object. Messages sent to nil are always allowed.
//
#if DEBUG
#define SAFE_RELEASE(x) \
    do { \
        [x release]; \
        if (!getenv("NSZombieEnabled")) \
            x = (id)0xDEADBEEF; \
    } while (0)
#else
#define SAFE_RELEASE(x) \
    [x release], x = nil
#endif

Код функционально эквивалентен второй версии Jano safeRelease, но добавляет документацию и соответствие Стандарты кодирования Google.

Ответ 3

Если есть вызов dealloc в нескольких местах, установка переменной объекта в nil гарантирует, что она не будет удаляться более одного раза по ошибке. Такая же логика, если функция с dealloc вызывается из более чем одного места или может быть вызвана произвольно внешним кодом (то есть другими классами).

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

Извините за общие черты.

Ответ 4

- (void)dealloc
{
     [searchPlace release];
     [super dealloc];
}
- (void)viewDidUnload
{
     [super viewDidUnload];
     self.searchPlace = nil;
}

Это похоже на то, что вы говорите?