Объект выделяет и инициализирует объект Objective C

В чем разница между следующими двумя способами выделения и инициализации объекта?

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

и

self.aController= [[AController alloc] init];

В большинстве примеров яблока используется первый метод. Зачем вам выделять, init и объект, а затем немедленно отпускать?

Ответ 1

Каждый объект имеет счетчик ссылок. Когда он переходит в 0, объект освобождается.

Предполагая, что свойство было объявлено как @property (retain):

Ваш первый пример, строка за строкой:

  • Объект создается alloc, он имеет счетчик ссылок 1.
  • Объект передается методу self setAController:, который отправляет ему сообщение retain (потому что метод не знает, откуда идет объект), увеличивая количество ссылок на 2.
  • Вызывающий код больше не нуждается в самом объекте, поэтому он вызывает release, уменьшая количество ссылок до 1.

В вашем втором примере в основном выполняются шаги 1 и 2, но не 3, поэтому в конце счетчик ссылок объекта равен 2.

Правило заключается в том, что если вы создаете объект, вы несете ответственность за его освобождение, когда вы закончите с ним. В вашем примере код выполняется с помощью tempAController после того, как он устанавливает свойство. Ответственность за метод setter заключается в вызове retain, если ему нужен этот объект.

Важно помнить, что self.property = foo; в Objective-C на самом деле является просто сокращением для [self setProperty:foo]; и что метод setProperty: будет сохранять или копировать объекты по мере необходимости.

Если свойство было объявлено @property (copy), тогда объект был бы скопирован, а не сохранен. В первом примере исходный объект будет выпущен сразу же; во втором примере исходный счетчик ссылок на объекты будет равен 1, хотя он должен быть равен 0. Таким образом, вы все равно хотите написать свой код таким же образом.

Если свойство было объявлено @property (assign), то self не требует владения объектом, а кому-то еще его нужно сохранить. В этом случае первый пример будет неправильным. Эти виды свойств редки, обычно используются только для делегатов объектов.

Ответ 2

Как отмечали другие, два фрагмента кода, которые вы показываете, не эквивалентны (по причинам управления памятью). Что касается выбора первого из последних:

Правильная формулировка последнего будет

self.aController= [[[AController alloc] init] autorelease];

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

Другая "возможная" реализация (в зависимости от того, откуда взялся этот пример):

aController = [[AController alloc] init];

Тем не менее, установка переменной экземпляра напрямую сильно не рекомендуется нигде, кроме метода init или dealloc. В другом месте вы всегда должны использовать методы доступа.

Это приводит нас к реализации, показанной в примере кода:

AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];

Это следует за лучшей практикой, поскольку:

  • Он избегает автообновления;
  • Это делает семантику управления памятью сразу же понятной;
  • Он использует метод доступа для установки переменной экземпляра.

Ответ 3

Обратите внимание также, что ваше желание сократить код до одной строки - это то, почему многие используют Autorelease:

self.aController = [[[AController alloc] init] autorelease];

Хотя теоретически на iPhone автореферат как-то дороже (никогда не слышал ясного объяснения, почему), и поэтому вы можете явно освободить сразу после назначения объекта в другом месте.

Ответ 4

Если вы используете Xcode, это может помочь вам обнаружить такой код со статическим анализатором. Просто нажмите Build → Build and Analyze

alt text

Это покажет вам очень полезное сообщение на таких фрагментах кода.

alt text

Ответ 5

Еще одна вещь, которую следует отметить, заключается в том, что ваш пример также зависит от определения @property aController.

Если он был определен как @property (readwrite, retain) id aController;, то ваш пример работает, а если он определен как @property (readwrite, assign) id aController;, то дополнительный вызов для освобождения приведет к освобождению вашего объекта.

Ответ 6

Вы также можете сделать

@property (nonatomic, retain)AController *aController;
...
self.aController= [[AController alloc] init];
[aController release];

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