Как работает пул автозапуска NSAutoreleasePool? - PullRequest
94 голосов
/ 15 сентября 2008

Насколько я понимаю, все, что создано с 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];
}

Ответы [ 7 ]

65 голосов
/ 15 сентября 2008

Да, ваш второй фрагмент кода действителен.

Каждый раз, когда -autorelease отправляется объекту, он добавляется в самый внутренний пул autorelease. Когда пул очищается, он просто отправляет -release всем объектам в пуле.

Автозапуск пулов - это просто удобство, которое позволяет отложить отправку -релизов до «позже». Это «позднее» может произойти в нескольких местах, но наиболее распространенным в приложениях GUI Cocoa является конец текущего цикла выполнения.

37 голосов
/ 08 октября 2008

NSAutoreleasePool: сток против выпуска

Поскольку функции drain и release, кажется, вызывают путаницу, здесь, возможно, стоит пояснить (хотя это описано в документации ...).

Строго говоря, с точки зрения общей картины drain не эквивалент release:

В среде с подсчетом ссылок drain выполняет те же операции, что и release, так что оба в этом смысле эквивалентны. Подчеркнем, это означает, что вы не пропускаете пул, если используете drain вместо release.

В среде сбора мусора, release - запрет. Таким образом, это не имеет никакого эффекта. drain, с другой стороны, содержит подсказку сборщику, что он должен «собрать при необходимости». Таким образом, в среде со сборщиком мусора использование drain помогает системным сборам баланса.

17 голосов
/ 03 ноября 2011

Как уже указывалось, ваш второй фрагмент кода правильный.

Я хотел бы предложить более краткий способ использования пула автоматического выпуска, который работает во всех средах (подсчет ссылок, GC, ARC), а также избегает путаницы утечки / выпуска:

int main(void) {
  @autoreleasepool {
    NSString *string;
    string = [[[NSString alloc] init] autorelease];
    /* use the string */
  }
}

В приведенном выше примере обратите внимание на блок @ autoreleasepool . Это задокументировано здесь .

7 голосов
/ 16 сентября 2008

Нет, вы не правы. В документации четко указано, что при не-GC -drain эквивалентен -release, то есть NSAutoreleasePool будет не пропущен.

0 голосов
/ 27 июня 2014

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

0 голосов
/ 20 февраля 2014

Что я читаю из Apple: «В конце блока пула автоматического выпуска объектам, получившим сообщение автоматического выпуска в блоке, отправляется сообщение об освобождении - объект получает сообщение об освобождении каждый раз, когда ему было отправлено сообщение автоматического выпуска в блоке».

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html

0 голосов
/ 15 сентября 2008

Да и нет. В конечном итоге вы освобождали бы строковую память, но «просачивали» объект 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 не создает экземпляры объектов, но вы все равно не отвечаете за них).

...