Как работает пул авто-релизов NSAutoreleasePool?
Насколько я понимаю, все, что создано с помощью alloc, new или copy, должно быть выпущено вручную. Например:
int main(void) {
NSString *string;
string = [[NSString alloc] init];
/* use the string */
[string release];
}
Мой вопрос, однако, разве это не было бы так же верно?
int main(void) {
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString *string;
string = [[[NSString alloc] init] autorelease];
/* use the string */
[pool drain];
}
9 ответов
Да, ваш второй фрагмент кода полностью действителен.
Каждый раз, когда -autorelease отправляется объекту, он добавляется в самый внутренний пул автоматического выпуска. Когда пул очищается, он просто отправляет -release всем объектам в пуле.
Автозапуск пулов - это просто удобство, которое позволяет отложить отправку -релизов до "позже". Это "позднее" может произойти в нескольких местах, но наиболее распространенным в приложениях GUI Какао является конец текущего цикла цикла выполнения.
NSAutoreleasePool: сток против выпуска
Поскольку функция drain
а также release
кажется, вызывает путаницу, может быть, стоит пояснить здесь (хотя это описано в документации...).
Строго говоря, с точки зрения общей картины drain
не эквивалентно release
:
В среде с подсчетом ссылок drain
выполняет те же операции, что и release
Таким образом, оба в этом смысле эквивалентны. Чтобы подчеркнуть, это означает, что вы не утечки пула, если вы используете drain
скорее, чем release
,
В мусорной среде, release
это неоперация. Таким образом, это не имеет никакого эффекта. drain
с другой стороны, содержит подсказку сборщику о том, что он должен "собирать при необходимости". Таким образом, в среде сбора мусора, используя drain
помогает системе сбалансировать сбор средств.
Как уже указывалось, ваш второй фрагмент кода правильный.
Я хотел бы предложить более краткий способ использования пула автоматического выпуска, который работает во всех средах (подсчет ссылок, GC, ARC), а также избегает путаницы утечки / выпуска:
int main(void) {
@autoreleasepool {
NSString *string;
string = [[[NSString alloc] init] autorelease];
/* use the string */
}
}
В приведенном выше примере обратите внимание на блок @autoreleasepool. Это задокументировано здесь.
Нет ты ошибаешься. В документации четко указано, что при не-GC -drain эквивалентен -release, то есть NSAutoreleasePool не будет пропущен.
Я обнаружил, что эта ссылка дала лучшее объяснение того, когда и как использовать NSAutoReleasePool: AutoReleasePool
Что я прочитал от Apple: "В конце блока пула автоматического выпуска объектам, получившим сообщение автоматического выпуска в блоке, отправляется сообщение об освобождении - объект получает сообщение об освобождении каждый раз, когда ему было отправлено сообщение автоматического выпуска в блоке. "
Отправка autorelease вместо релиза объекту продлевает время жизни этого объекта, по крайней мере, до тех пор, пока сам пул не будет очищен (может быть, если объект впоследствии будет сохранен). Объект может быть помещен в один и тот же пул несколько раз, и в этом случае он получает сообщение о выпуске каждый раз, когда его помещают в пул.
Да, ваш код идеален, если вы будете использовать сборщик мусора, достаточно будет просто установить строку в ноль, когда вы закончите с ней. Сборка мусора плохо влияет на производительность вашего приложения, поэтому я бы не рекомендовал его использовать:P
И да и нет. В конечном итоге вы освободите строковую память, но "утечете" объект NSAutoreleasePool в память, используя слив вместо освобождения, если вы запустили это в среде со сборкой мусора (не управляемой памятью). Эта "утечка" просто делает экземпляр NSAutoreleasePool "недоступным", как и любой другой объект без сильных указателей под GC, и объект будет очищен при следующем запуске GC, что вполне может быть сразу после вызова -drain
:
истощать
В среде с сборкой мусора запускает сборку мусора, если память выделена с момента последнего сбора, превышающего текущий порог; в противном случае ведет себя как выпуск. ... В среде со сборщиком мусора этот метод в конечном итоге вызывает
objc_collect_if_needed
,
В противном случае, это похоже на то, как -release
ведет себя не-GC, да. Как говорили другие, -release
не работает под GC, поэтому единственный способ убедиться, что пул правильно работает под GC, - это -drain
, а также -drain
под не-GC работает так же, как -release
под не-GC, и, возможно, более четко передает его функциональность.
Я должен отметить, что ваше утверждение "все, что вызывается с new, alloc или init" не должно включать "init" (но должно включать "copy"), потому что "init" не выделяет память, он только устанавливает объект (конструктор) мода). Если вы получили объект alloc'd и ваша функция вызывала только init как таковой, вы бы не освободили его:
- (void)func:(NSObject*)allocd_but_not_init
{
[allocd_but_not_init init];
}
Это не потребляет больше памяти, чем вы уже начали (при условии, что init не создает экземпляры объектов, но вы все равно не отвечаете за них).