Использовать авто-релиз перед добавлением объектов в коллекцию? - PullRequest
5 голосов
/ 18 июля 2009

Я просматривал вопросы, заданные в StackOverflow, но в Objective-C столько всего связано с управлением памятью, что я не смог найти ответ, который искал.

Вопрос в том, можно ли (и рекомендуется) вызывать autorelease перед добавлением вновь созданного объекта в коллекцию (например, NSMutableArray)? Или я должен выпустить это явно после добавления. (Я знаю, NSMutableArray сохранит объект)

Это иллюстрирует мой вопрос:

Сценарий A (авто-выпуск):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[[MyClass alloc] init] autorelease];

   [array addObject:obj];
}

Сценарий B (явный выпуск):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[MyClass alloc] init];

   [array addObject:obj];

   [obj release];
}

Я предполагаю, что оба верны, но я не уверен, и я точно не знаю, какой предпочтительный способ.

Может ли гуру Objective-C пролить свет на это?

Ответы [ 7 ]

12 голосов
/ 19 июля 2009

ИМХО, какой путь «правильный», является вопросом предпочтения. Я не согласен с респондентами, которые выступают за то, чтобы не использовать autorelease, но я предпочитаю использовать autorelease, если для этого нет веских причин. Я перечислю свои причины, и вы сможете решить, соответствуют ли они вашему стилю программирования.

Как указал Чак, существует полугородская легенда о том, что использование пусков автоматического выпуска связано с некоторыми накладными расходами. Это не может быть дальше от истины, и это происходит из-за бесчисленных часов, проведенных с использованием Shark.app, чтобы выжать последний бит производительности из кода. Попытка оптимизировать это глубоко на «преждевременной оптимизации» территории. Если и только если Shark.app предоставит вам достоверные данные о том, что это может быть проблемой, если вы даже подумаете об этом.

Как отмечали другие, автоматически выпущенный объект "освобождается в какой-то более поздний момент". Это означает, что они задерживаются, забирая память, пока эта «точка отсчета» не перевернется. В «большинстве» случаев это находится в нижней части прохода обработки события, прежде чем цикл выполнения переходит в спящий режим до следующего события (таймер, пользователь что-то щелкает и т. Д.).

Иногда, однако, вам придется избавляться от этих временных объектов раньше, чем позже. Например, вам нужно обработать огромный многомегабайтный файл или десятки тысяч строк из базы данных. Когда это происходит, вам нужно поместить NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; в хорошо выбранную точку, а затем [pool release]; внизу. Это почти всегда происходит при некоторой «пакетной обработке цикла», поэтому обычно это начало и конец некоторого критического цикла. Опять же, это должно быть основано на доказательствах, а не на догадках. ObjectAlloc от Instrument.app - это то, что вы используете для поиска этих проблем.

Основная причина, по которой я предпочитаю autorelease, а не release, заключается в том, что на 1015 * намного проще писать программы без утечек. Короче говоря, если вы решите пойти по маршруту release, вам необходимо гарантировать , что release в конечном итоге будет отправлено на obj, под all обстоятельства. Хотя это может показаться простым, на практике это удивительно сложно. Возьмите ваш пример, например:

   // array is an instance of NSMutableArray
   MyClass *obj = [[MyClass alloc] init];
   [array addObject:obj];
   // Assume a few more lines of work....
   [obj release];

Теперь представьте, что по какой-то причине что-то где-то слегка нарушает ваше предположение, что array является изменчивым, возможно, в результате использования какого-либо метода для обработки результатов, и возвращенный массив, содержащий обработанные результаты, был создан как NSArray. Когда вы отправляете addObject: на этот неизменный NSArray, возникает исключение, и вы никогда не отправите obj его release сообщение. Или, может быть, что-то идет не так, когда между obj было alloc d и требуется вызов release, как будто вы проверили какое-то условие и return() сразу по ошибке, потому что ваше мнение о том, что это Позвоните release позже обязательно .

Вы только что слили объект. И, вероятно, подписался на несколько дней, пытаясь выяснить, где и почему это ваша утечка. Исходя из опыта, вы потратите много часов на просмотр этого кода выше, убедившись, что он не может быть источником утечки, потому что вы очень четко отправили obj a release. Затем, через несколько дней, вы почувствуете то, что можно назвать только религиозным прозрением, поскольку вы понимаете причину проблемы.

Рассмотрим случай autorelease:

   // array is an instance of NSMutableArray
   MyClass *obj = [[[MyClass alloc] init] autorelease];
   [array addObject:obj];
   // Assume a few more lines of work....

Теперь, больше не имеет значения, что происходит, потому что практически невозможно протечь obj случайно, даже в чрезвычайно необычных или исключительных случаях.

7 голосов
/ 18 июля 2009

Оба верны и будут работать так, как вы ожидаете.

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

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

3 голосов
/ 18 июля 2009

Они оба верны, но B может быть предпочтительнее, потому что у него вообще нет служебных данных. Авто-релиз заставляет пул авто-релиза брать на себя ответственность за объект. Это имеет очень небольшие накладные расходы, которые, конечно, умножаются на количество задействованных объектов.

Таким образом, с одним объектом A и B более или менее одинаковы, но определенно не используйте A в сценариях с большим количеством объектов для добавления в массив.

В разных ситуациях автоматическое освобождение может задерживать и накапливать освобождение многих объектов в конце потока. Это может быть неоптимальным. Позаботьтесь о том, чтобы в любом случае автоматическое освобождение происходило без явного вмешательства. Например, многие геттеры реализованы следующим образом:

return [[myObject retain] autorelease];

поэтому, когда вы вызываете геттер, вы добавляете объект в пул автоматического выпуска.

0 голосов
/ 02 мая 2011

Я предпочитаю A (авто-релиз) для краткости и «безопасности», как называет это Джон. Это упрощает мой код, и у меня никогда не было проблем с ним.

То есть до сегодняшнего дня: у меня была проблема с автоматическим выпуском блока перед добавлением его в массив. Смотрите мой вопрос: [myArray addObject: [[objcBlock copy] autorelease]] аварийно завершает работу при освобождении массива (Обновление: выясняется, что проблема была в другом месте в моем коде, но все же была небольшая разница в поведении с autorelease ...)

0 голосов
/ 18 июля 2009

Они оба в порядке. Некоторые люди скажут вам избегать автоматического выпуска из-за «накладных расходов» или чего-то подобного, но на самом деле накладных расходов практически нет. Идите вперед и сравните это и попытайтесь найти «накладные расходы». Единственная причина, по которой вы избежите этого, заключается в ситуации с нехваткой памяти, как на iPhone. В OS X у вас практически неограниченная память, поэтому это не будет иметь большого значения. Просто используйте тот, который вам удобнее.

0 голосов
/ 18 июля 2009

Вы alloc 'отредактировали объект, и ваша задача в какой-то момент выпустить его. Оба фрагмента кода работают одинаково, правильно, причем способ autorelease является потенциально более медленным аналогом.

Лично я предпочитаю способ autorelease, так как его легче набирать и он почти никогда не является узким местом.

0 голосов
/ 18 июля 2009

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

http://macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html?page=last&x-showcontent=text

...