Распределение и инициализация объекта в Задаче C - PullRequest
56 голосов
/ 01 октября 2008

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

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

и

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

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

Ответы [ 6 ]

70 голосов
/ 01 октября 2008

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

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

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

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

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

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

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

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

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

30 голосов
/ 03 октября 2008

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

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

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

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

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

aController = [[AController alloc] init];

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

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

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

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

  • Избегает авто-релиза;
  • Немедленно проясняет семантику управления памятью;
  • Он использует метод доступа для установки переменной экземпляра.
5 голосов
/ 14 января 2011

Если вы используете XCode, он может помочь вам обнаружить такой код с помощью статического анализатора. Просто нажмите Build >> Build and Analyze

alt text

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

alt text

5 голосов
/ 01 октября 2008

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

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

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

4 голосов
/ 01 октября 2008

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

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

2 голосов
/ 04 августа 2009

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

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

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

...