Как работает пул автозапуска NSAutoreleasePool?

95

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

Ответы:

68

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

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

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

кперрюа
источник
5
где конец текущего цикла цикла выполнения, если у меня нет цикла?
Спасибо,
24
Разве «самый внешний» не должен быть «самым внутренним»?
Майк Веллер
an objectдолжно быть an object that is a subclass of NSObject or NSProxy and doesn't override -autorelease.
1
РЕДАКТИРОВАТЬ: изменено самое внешнее на самое внутреннее.
чакрит
1
Важно: Если вы используете автоматический подсчет ссылок (ARC), вы не можете напрямую использовать пулы с автоматическим выпуском. Вместо этого вы используете блоки @autoreleasepool. От developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Md Mahbubur Rahman
37

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

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

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

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

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

mmalc
источник
4
«Утечка» принципиально невозможна NSAutoreleasePool. Это потому, что пулы работают как стек. Создание экземпляра пула подталкивает этот пул к вершине стека пула автоматического освобождения потоков. -releaseзаставляет этот пул выталкиваться из стека И любых пулов, которые были помещены поверх него, но по какой-либо причине не были вытолкнуты.
Джон
7
Каким образом это относится к тому, что я написал?
mmalc
2
Мне нравится, как он нашел время, чтобы выделить И. ЩЕЛЧОК!
Билли Грей
17

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

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

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

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

Неовибрант
источник
2
Обратите внимание, что автоматический выпуск не разрешен с ARC.
dmirkitanov
1
Чтобы уточнить, нужно использовать @autoreleasepoolблок с ARC.
Саймон
7

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

кперрюа
источник
Мне было интересно, почему Xcode генерирует код с -drain, если это так. Я использовал -drain, потому что думал, что он эквивалентен -release на основе кода, сгенерированного Xcode.
Джеймс Самнерс,
1
Принципиально невозможно «просочить» a NSAutoreleasePool: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…
Джон
0

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

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

Gagan_iOS
источник
0

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

Хардик Мамтора
источник
-2

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

Лорен Сигал
источник
Мне неудобно оставлять этот ответ принятым, если ваша информация о сливе не совсем верна. См. Developer.apple.com/documentation/Cocoa/Reference/Foundation/… Обновите, и я снова приму.
Джеймс Самнерс,
Что неточного в ответе? В среде со сборкой мусора (как указано) слив не удаляет пул AutoReleasePool, поэтому будет происходить утечка памяти, если вы не использовали release. Цитата, которую я перечислил, была прямо из уст лошади, документы на сливе.
Лорен Сигал,
1
Лорен: В сборке мусора - [NSAutoreleasePool Drain] вызовет сбор. -retain, -release и -autorelease игнорируются сборщиком; вот почему -drain используется в пулах автозапуска под GC.
Крис Хэнсон,
В документации по «сливу»: в среде с управляемой памятью это ведет себя так же, как вызов release. Таким образом, у вас не будет утечки памяти, если вы будете использовать «слив» вместо выпуска.
mmalc
-[NSAutoreleasePool release]в среде со сборкой мусора это не работает. -[NSAutoreleasePool drain]работает как в средах с подсчетом ссылок, так и со сборкой мусора.
Джонатан Стерлинг,