Размещение и инициализация объекта в Objective C

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

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

и

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

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


person Ronnie Liew    schedule 01.10.2008    source источник


Ответы (6)


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

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

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

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

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

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

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

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

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

person benzado    schedule 01.10.2008
comment
Это только в том случае, если 'aController' объявлен с @property (сохранить), правда? - person ; 01.10.2008
comment
Это также правильный образец, если свойство объявлено (копирование). Второй шаблон верен для объявленного свойства (assign) (или с включенной сборкой мусора). - person Jens Ayton; 01.10.2008
comment
Это принятый ответ, но я настоятельно рекомендую вам также прочитать ответ mmalc ниже. Он показывает, как сделать это более практичным образом (с крошечным, крошечным, крошечным эффектом производительности). - person Adam; 06.08.2010
comment
вау, я не знал, что объективный подсчет ссылок C был так плохо реализован. Даже c ++ справляется с этим лучше с shared_ptr (для начала нет нелепой необходимости назначать темпоралам) - person lurscher; 27.05.2012
comment
@lurscher С тех пор, как об этом спросили и ответили, многое изменилось; прочтите об автоматическом подсчете ссылок, если вы действительно заинтересованы в управлении памятью в Objective-C (а не только здесь, чтобы опубликовать snark). - person benzado; 28.05.2012
comment
@benzado, уже узнал об этом и обнаружил, что Pimpl со скрытыми реализациями объектов C ++ в .mm - лучший подход для моего требования, спасибо за ответ, .. Кстати, в snark нет ничего плохого, когда это правда;) - person lurscher; 28.05.2012
comment
@lurscher Что плохого в snark, так это то, что он заставляет вас походить на плохого инженера. - person benzado; 28.05.2012

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

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

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

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

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

aController = [[AController alloc] init];

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

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

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

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

  • Это позволяет избежать автоспуска;
  • Это сразу же проясняет семантику управления памятью;
  • Он использует метод доступа для установки переменной экземпляра.
person mmalc    schedule 03.10.2008
comment
mmalc правильно. Но ... на практике 3 строки кода для присвоения 1 переменной просто безумие. Большинство команд, которые, как я видел, используют автозапуск, делают все, используя одну строку кода - накладные расходы УНИЖАЮЩИЕ малы. Если вы когда-нибудь столкнетесь с ситуацией на iPhone, когда это приведет к значительной разнице в производительности, вам, вероятно, все равно стоит переписать этот код на C - вы делаете слишком много выделения. Немногие команды будут придерживаться официального соглашения (это правильно, ИМХО - реальные программы должны быть четкими, но не до словесного поноса). - person Adam; 06.08.2010

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

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

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

person Kendall Helmstetter Gelner    schedule 01.10.2008
comment
Не думаю, что это дороже, чем автозапуск в Какао. Просто автоспуск дороже релиза. Поэтому, если вы можете освободить объект вместо автоматического освобождения, вы должны это сделать. - person schwa; 01.10.2008
comment
autorelease дороже, потому что он должен найти локальную структуру данных потока и добавить к ней (пул autorelease), тогда как release просто уменьшает целое число (счетчик ссылок). - person benzado; 02.10.2008
comment
@benzado: Да, вопрос был в том, почему автозапуск дороже на iPhone, а не почему он дороже релиза. - person Sergio Acosta; 11.10.2008

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

alt text

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

alt text

person leviathan    schedule 14.01.2011

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

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

person Ashley Clark    schedule 01.10.2008
comment
Нет смысла указывать чтение и запись, поскольку это значение по умолчанию. - person mk12; 04.08.2009

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

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

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

person mk12    schedule 04.08.2009
comment
Было бы еще хуже, если бы это была копирующая собственность. Вы бы освободили копию, оставив мертвый объект в переменной экземпляра и выдавая объект, который вы выделили и инициировали. - person Peter Hosey; 04.08.2009
comment
Но он работает, если он сохраняется. Не то чтобы тебе следовало это делать. - person mk12; 17.08.2009
comment
Я на самом деле думаю, что это хороший подход, если вы знаете, что это свойство сохраняемое. Это немного убирает беспорядок в коде. - person andrewz; 16.03.2011