Выделение и инициализация объекта в Objective C
-
03-07-2019 - |
Вопрос
В чем разница между следующими двумя способами выделения и инициализации объекта?
AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];
и
self.aController= [[AController alloc] init];
В большинстве примеров с яблоком используется первый метод.Зачем вам выделять, инициализировать и объект, а затем немедленно освобождать?
Решение
Каждый объект имеет счетчик ссылок.Когда он становится равным 0, объект освобождается.
Если предположить, что имущество было заявлено как @property (retain)
:
Ваш первый пример, построчно:
- Объект создан
alloc
, его счетчик ссылок равен 1. - Объект передан в
self
'ssetAController:
метод, который отправляет емуretain
сообщение (поскольку метод не знает, откуда поступает объект), увеличивая счетчик ссылок до 2. - Вызывающему коду больше не нужен сам объект, поэтому он вызывает
release
, уменьшая счетчик ссылок до 1.
Ваш второй пример в основном выполняет шаги 1 и 2, но не 3, поэтому в конце счетчик ссылок объекта равен 2.
Правило таково: если вы создаете объект, вы несете ответственность за его освобождение после завершения работы с ним.В вашем примере код выполняется с помощью tempAController после установки свойства.Ответственность за вызов метода установки лежит на retain
если ему нужно, чтобы этот объект оставался рядом.
Важно помнить это self.property = foo;
в Objective-C на самом деле просто сокращение от [self setProperty:foo];
и что setProperty:
метод будет сохранять или копировать объекты по мере необходимости.
Если имущество задекларировано @property (copy)
, то объект был бы скопирован, а не сохранен.В первом примере исходный объект будет освобожден сразу;во втором примере счетчик ссылок исходного объекта будет равен 1, хотя он должен быть равен 0.Таким образом, вы все равно захотите написать свой код таким же образом.
Если имущество задекларировано @property (assign)
, затем self
не претендует на право собственности на объект, и кто-то другой должен сохранить его.В этом случае первый пример будет неверным.Свойства такого типа встречаются редко и обычно используются только для делегатов объектов.
Другие советы
Как отмечали другие, два показанных вами фрагмента кода не эквивалентны (по причинам управления памятью).Что касается того, почему первое выбрано вместо второго:
Правильная формулировка последнего будет такой:
self.aController= [[[AController alloc] init] autorelease];
По сравнению с первым, это добавляет дополнительные накладные расходы из-за использования пула автоматического выпуска и в некоторых случаях приводит к неоправданному продлению времени жизни объекта (пока не будет освобожден пул автоматического выпуска), что увеличит объем памяти вашего приложения.
Другая «возможная» реализация (в зависимости от того, откуда взят пример) проста:
aController = [[AController alloc] init];
Однако установка переменной экземпляра напрямую категорически не рекомендуется где-либо, кроме методов init или Dealloc.В других местах вы всегда должны использовать методы доступа.
Это подводит нас к реализации, показанной в примере кода:
AController *tempAController = [[AController alloc] init];
self.aController = tempAController;
[tempAController release];
Это соответствует передовой практике, поскольку:
- Это позволяет избежать автоматического выпуска;
- Это сразу делает семантику управления памятью понятной;
- Он использует метод доступа для установки переменной экземпляра.
Также обратите внимание, что ваше желание сократить код до одной строки является причиной того, что многие люди используют Autorelease:
self.aController = [[[AController alloc] init] autorelease];
Хотя теоретически на iPhone автовыпуск как-то дороже (никогда не слышал четкого объяснения, почему), и поэтому вы можете захотеть явно выпустить сразу после того, как назначите объект в другом месте.
Если вы используете Xcode, это может помочь вам обнаружить такой код с помощью статического анализатора.Просто нажмите «Создать» >> «Построить и проанализировать».
Это покажет вам очень полезное сообщение в таких фрагментах кода.
Еще одна вещь, на которую следует обратить внимание: ваш пример также зависит от определения @property для aController.
Если бы это было определено как @property (readwrite, retain) id aController;
тогда ваш пример работает, а если он определен как @property (readwrite, assign) id aController;
тогда дополнительный вызов Release приведет к освобождению вашего объекта.
Вы также можете сделать
@property (nonatomic, retain)AController *aController;
...
self.aController= [[AController alloc] init];
[aController release];
с сохраняющим свойством, и он будет функционировать таким же образом, но лучше использовать другой способ (для сохранения свойств), потому что это менее запутанно, этот код выглядит так, как будто вы назначаете aController, а затем он удаляется из памяти, когда на самом деле это не так, потому что setAController сохраняет его.