Использовать авто-релиз перед добавлением объектов в коллекцию?
Я просматривал вопросы, заданные в Stackru, но в 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 ответов
Оба являются правильными и будут работать так, как вы ожидаете.
Лично я предпочитаю использовать последний метод, но только потому, что мне нравится четко указывать, когда объекты будут освобождены. При автоматическом освобождении объекта все, что мы делаем, это говорим, что "этот объект будет выпущен в какой-то произвольный момент в будущем". Это означает, что вы можете поместить автоматически освобожденный объект в массив, уничтожить массив, и объект может (вероятно) все еще существовать.
При последнем методе объект будет немедленно уничтожен массивом (при условии, что ничего больше не появилось и не сохранило его за это время). Если я нахожусь в среде с ограниченным объемом памяти (скажем, iPhone), где мне нужно быть осторожным с тем, сколько памяти я использую, я воспользуюсь последним способом, просто чтобы у меня не оставалось слишком много объектов NSAutoreleasePool где-то. Если использование памяти не является для вас большой проблемой (и, как правило, не для меня), то любой из этих методов полностью приемлем.
ИМХО, какой путь "правильный", это вопрос предпочтений. Я не согласен с респондентами, которые выступают за не использование autorelease
, но я предпочитаю использовать autorelease
если нет убедительной причины не делать этого. Я перечислю свои причины, и вы сможете решить, соответствуют ли они вашему стилю программирования.
Как указал Чак, существует полугородская легенда о том, что использование пусков авто-релизов связано с некоторыми накладными расходами. Это не может быть дальше от истины, и это происходит из-за бесчисленных часов, проведенных с использованием Shark.app, чтобы выжать последний бит производительности из кода. Попытка оптимизировать это глубоко на "преждевременной оптимизации" территории. Если и только если Shark.app предоставит вам точные данные о том, что это может быть проблемой, если вы даже подумаете над этим.
Как указывали другие, автоматически выпущенный объект "освобождается в более поздний момент". Это означает, что они задерживаются, забирая память, пока эта "поздняя точка" не перевернется. В "большинстве" случаев это находится в нижней части прохода обработки события, прежде чем цикл выполнения переходит в спящий режим до следующего события (таймер, пользователь что-то щелкает и т. Д.).
Однако иногда вам нужно будет избавиться от этих временных объектов раньше, чем позже. Например, вам нужно обработать огромный многомегабайтный файл или десятки тысяч строк из базы данных. Когда это произойдет, вам нужно будет NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
в хорошо выбранной точке, сопровождаемой [pool release];
внизу. Это почти всегда происходит при некоторой "пакетной обработке цикла", поэтому обычно это начало и конец некоторого критического цикла. Опять же, это должно быть основано на доказательствах, а не на догадках. ObjectAlloc от Instrument.app - это то, что вы используете, чтобы найти эти проблемные места.
Основная причина, почему я предпочитаю autorelease
в release
Однако гораздо проще писать программы без утечек. Короче говоря, если вы решите пойти release
маршрут, вы должны гарантировать, что release
в конечном итоге отправляется obj
при любых обстоятельствах. Хотя это может показаться простым, на практике это удивительно сложно. Возьмите ваш пример, например:
// 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
г и требуемый вызов release
как будто вы проверите некоторое состояние и return()
сразу по ошибке, потому что это ускользнуло от вас, что этот призыв к release
позже должно произойти.
Вы только что слили объект. И, вероятно, подписался на несколько дней, пытаясь выяснить, где и почему это ваша утечка. Исходя из своего опыта, вы потратите много часов на просмотр кода выше, убедившись, что он не может быть источником утечки, потому что вы очень четко obj
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
случайно, даже в чрезвычайно необычных или исключительных случаях.
Они оба верны, но B может быть предпочтительнее, потому что у него вообще нет накладных расходов. Авто-релиз заставляет пул авто-релиза брать на себя ответственность за объект. Это имеет очень небольшие накладные расходы, которые, конечно, умножаются на количество задействованных объектов.
Таким образом, с одним объектом A и B более или менее одинаковы, но определенно не используйте A в сценариях с большим количеством объектов для добавления в массив.
В различных ситуациях автоматическое освобождение может задерживать и накапливать освобождение многих объектов в конце потока. Это может быть неоптимальным. Позаботьтесь о том, чтобы в любом случае автоматическое освобождение происходило без явного вмешательства. Например, многие геттеры реализованы следующим образом:
return [[myObject retain] autorelease];
поэтому всякий раз, когда вы вызываете геттер, вы добавляете объект в пул автоматического выпуска.
Они оба в порядке. Некоторые люди скажут вам избегать автоматического выпуска из-за "накладных расходов" или чего-то подобного, но на самом деле накладных расходов практически нет. Идите вперед и сравните его и попытайтесь найти "накладные расходы". Единственная причина, по которой вы избежите этого, заключается в ситуации с нехваткой памяти, как на iPhone. На OS X у вас практически неограниченная память, поэтому это не будет иметь большого значения. Просто используйте тот, который вам удобнее.
Я предпочитаю A (авто-релиз) для краткости и "безопасности", как называет это Джон. Это упрощает мой код, и у меня никогда не было проблем с ним.
То есть до сегодняшнего дня: у меня была проблема с автоматическим выпуском блока перед добавлением его в массив. См. Мой вопрос о стекопереработке: [myArray addObject: [[objcBlock copy] autorelease]] аварийно завершает работу при освобождении массива (обновление: выясняется, что проблема была в другом месте в моем коде, но все же была небольшая разница в поведении с автоматическим выпуском…)
У тебя есть alloc
Вы можете освободить его в какой-то момент. Оба фрагмента кода работают одинаково, правильно, с autorelease
способ быть потенциально более медленным аналогом.
Лично я предпочитаю autorelease
Кстати, так как просто набрать текст и почти никогда не является узким местом.
Вы можете отправить autorelease
сообщение в любой момент, потому что оно не действует до тех пор, пока цикл сообщения приложения не повторится (т. е. пока все ваши методы не завершат выполнение в ответ на пользовательский ввод).
http://macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html?page=last&x-showcontent=text