Почему @autoreleasepool все еще нуждается в ARC?

В большинстве случаев с ARC (Automatic Reference Counting) нам вообще не нужно думать об управлении памятью с объектами Objective-C. Невозможно создать NSAutoreleasePool больше, однако есть новый синтаксис:

@autoreleasepool {
    …
}

Мой вопрос: зачем мне это когда-либо понадобиться, когда я не должен вручную выпускать/автореализоваться?


РЕДАКТИРОВАТЬ: Подводя итог тому, что я получил из всех андерверов и комментариев лаконично:

Новый синтаксис:

@autoreleasepool { … } - новый синтаксис для

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];

Более важно:

  • ARC использует autorelease, а также release.
  • Для этого нужен пул автоматического выпуска.
  • ARC не создает для вас пул автовыпусков. Однако:
    • В главном потоке каждого приложения Cocoa уже есть пул автозаполнения.
  • Есть два случая, когда вы можете использовать @autoreleasepool:
    • Когда вы находитесь во вторичном потоке и нет пула автовыпусков, вы должны сделать свой собственный, чтобы предотвратить утечки, такие как myRunLoop(…) { @autoreleasepool { … } return success; }.
    • Если вы хотите создать более локальный пул, как показал @mattjgalloway в своем ответе.

Ответ 1

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

Одно из других изменений, которые они внесли с новым компилятором Clang 3.0 и ARC, заключается в том, что они заменили NSAutoReleasePool на директиву @autoreleasepool. NSAutoReleasePool всегда был немного особенным "объектом", и они сделали это так, чтобы синтаксис использования одного не смешивался с объектом, так что он в целом был более простым.

В принципе, вам нужно @autoreleasepool, потому что по-прежнему есть пулы автозапуска, о которых можно беспокоиться. Вам просто не нужно беспокоиться о добавлении вызовов autorelease.

Пример использования пула автоматического выпуска:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

Наверняка надуманный пример, конечно, но если у вас не было @autoreleasepool внутри внешнего for -loop, тогда вы будете выпускать 100000000 объектов позже, а не 10000 каждый раз вокруг внешнего for -loop.

Update: Также см. Этот ответ - fooobar.com/questions/36711/... - почему @autoreleasepool не имеет никакого отношения к ARC.

Update: Я просмотрел внутренности того, что происходит здесь, и написал это в моем блоге. Если вы посмотрите там, вы увидите, что именно делает ARC и как новый стиль @autoreleasepool и как он вводит область применения, используется компилятором для вывода информации о том, что нужно сохранить, освободить и автореализовать.

Ответ 2

@autoreleasepool ничего не делает. Он создает пул автозапуска, так что, когда достигнут конец блока, любые объекты, которые были автореализованы ARC при активном блоке, будут отправлены сообщения о выпуске. Apple Руководство по программированию с расширенным управлением памятью объясняет это следующим образом:

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

Ответ 3

Люди часто неправильно понимают ARC для какой-то сборки мусора или тому подобного. По правде говоря, спустя некоторое время люди в Apple (благодаря проектам llvm и clang) поняли, что управление памятью Objective-C (все retains и releases и т.д.) Можно полностью автоматизировать во время компиляции. Это, просто прочитав код, еще до его запуска!:)

Для этого существует только одно условие: мы ДОЛЖНЫ следовать rules, иначе компилятор не сможет автоматизировать процесс во время компиляции. Поэтому, чтобы гарантировать, что мы никогда не нарушаем правила, нам не разрешается явно писать release, retain и т.д. Эти вызовы автоматически вводятся в наш код компилятором. Поэтому внутри мы все еще имеем autorelease s, retain, release и т.д. Просто нам больше не нужно их писать.

A ARC автоматически во время компиляции, что намного лучше, чем во время выполнения, например сбор мусора.

У нас все еще есть @autoreleasepool{...}, потому что, если он не нарушает никаких правил, мы бесплатно создаем/удаляем наш пул в любое время, когда нам это нужно:).

Ответ 4

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

Ответ 5

Цитата из https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:

Блоки и потоки пулов Autorelease

Каждый поток в приложении Cocoa сохраняет свой собственный стек блоки автобизнеса. Если вы пишете программу только для Фонда или если вы отсоедините поток, вам необходимо создать свою собственную авторекламу пул блоков.

Если ваше приложение или поток долговечны и потенциально генерируют много автореализованных объектов, вы должны использовать блоки пула авторекламы (например, AppKit и UIKit делают на основном потоке); в противном случае, автореализован объекты накапливаются, а объем памяти увеличивается. Если ваш отдельный поток не вызывает вызовы Cocoa, вам не нужно использовать блок пула авторезистов.

Примечание. Если вы создаете дополнительные потоки с использованием API-интерфейсов потоков POSIX вместо NSThread вы не можете использовать Cocoa, если только Cocoaмногопоточность. Cocoa переходит в режим многопоточности только после отделяя свой первый объект NSThread. Использовать Cocoa для вторичного POSIX потоки, ваше приложение должно сначала отсоединить хотя бы один NSThread объект, который может немедленно выйти. Вы можете проверить, находится ли Cocoaмногопоточность с методом класса NSThread isMultiThreaded.

...

В автоматическом подсчете ссылок или ARC система использует тот же система подсчета ссылок как MRR, но вставляет соответствующую память вызов метода управления для вас во время компиляции. Вы сильно рекомендуется использовать ARC для новых проектов. Если вы используете ARC, обычно нет необходимости понимать базовую реализацию описанных в этом документе, хотя в некоторых ситуациях это может быть полезно. Дополнительные сведения о ARC см. В разделе Переход к примечаниям о выпуске ARC.

Ответ 6

Похоже, что существует большая путаница в этой теме (и, по крайней мере, 80 человек, которые, вероятно, сейчас смущены этим и считают, что им нужно посыпать @autoreleasepool вокруг своего кода).

Если проект (включая его зависимости) использует исключительно ARC, то @autoreleasepool никогда не должен использоваться и не будет полезен. ARC будет обрабатывать выпуски объектов в нужное время. Например:

@interface Testing: NSObject
+ (void) test;
@end

@implementation Testing
- (void) dealloc { NSLog(@"dealloc"); }

+ (void) test
{
    while(true) NSLog(@"p = %p", [Testing new]);
}
@end

отображается:

p = 0x17696f80
dealloc
p = 0x17570a90
dealloc

Каждый объект тестирования освобождается, как только значение выходит за пределы области видимости, не дожидаясь выхода из пула авторесурсов. (То же самое происходит с примером NSNumber, это просто позволяет нам наблюдать за dealloc.) ARC не использует autorelease.

Причина @autoreleasepool по-прежнему разрешена для смешанных проектов ARC и не ARC, которые еще не полностью перешли на ARC.

Если вы вызываете код без ARC, он может вернуть объект с автореализацией. В этом случае вышеуказанный цикл будет протекать, поскольку текущий пул авторесурсов никогда не будет удален. Это где вы хотите поставить @autoreleasepool вокруг блока кода.

Но если вы полностью сделали переход ARC, тогда забудьте об autoreleasepool.