Почему @autoreleasepool все еще нужен с ARC?


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

@autoreleasepool {
    …
}

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


EDIT: чтобы подвести итог тому, что я получил из всех anwers и комментариев лаконично:

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

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

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

что еще более важно:

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

7 ответов:

Arc не избавиться от сохраняет, релизы и autoreleases, он просто добавляет в необходимом для вас. Так что есть еще вызовы, чтобы сохранить, есть еще призывы к освобождению, есть еще вызовы на 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-цикл, то вы будете выпускать 100000000 объекты позже, а не 10000 каждый раз вокруг внешней for-петли.

обновление: Также смотрите этот ответ -https://stackoverflow.com/a/7950636/1068248 - за что @autoreleasepool не имеет ничего общего с дуги.

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

@autoreleasepool ничего не autorelease. Он создает пул авторелиза, так что при достижении конца блока все объекты, которые были автоматически выпущены ARC, пока блок был активен, будут отправлены сообщения о выпуске. Яблоко Advanced Memory Management Programming Guide объясняет это таким образом:

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

люди часто неправильно понимают ARC для какой-то сборки мусора или тому подобное. Правда в том, что через некоторое время люди в Apple (благодаря проектам llvm и clang) поняли, что Objective-c's memory administration (all the retains и releases и т. д.) может быть полностью автоматизирован в время компиляции. Это, просто читая код, даже до его запуска! :)

для этого есть только одно условие: мы должны следовать правила, в противном случае компилятор не сможет автоматизировать процесс компиляции. Итак, чтобы убедиться, что мы никогда нарушайте правила, нам не разрешается явно писать release,retain и т. д. Эти вызовы автоматически вводятся в наш код компилятором. Следовательно, внутренне мы все еще имеем autorelease s,retain,release и т. д. Просто нам больше не нужно их писать.

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

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

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

процитировано https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:

Autorelease пул блоков и нитей

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

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

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

...

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

Autorelease бассейны, необходимых для возвращения вновь создаваемых объектов с помощью метода. Например, рассмотрим этот фрагмент кода:

- (NSString *)messageOfTheDay {
    return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}

строка, созданная в методе будет иметь сохранить количество единиц. Теперь кто должен уравновесить то, что сохраняет счет с освобождением?

сам метод? Невозможно, он должен вернуть созданный объект, поэтому он не должен освобождать его до возвращения.

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

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

вот почему есть autorelease пулы, поэтому первый способ будет на самом деле стань

- (NSString *)messageOfTheDay {
    NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
    return [res autorelease];
}

вызов autorelease на объект добавляет его в autorelease пул, но что это действительно означает, добавив объект в autorelease пул? Ну, это значит сказать вашей системе"я хочу, чтобы вы выпустили этот объект для меня, но в какой-то более позднее время, а не сейчас; у него есть счетчик сохранения, который должен быть сбалансирован выпуском, иначе память будет течь, но я не могу сделать это сам прямо сейчас, так как мне нужно, чтобы объект оставался живым за пределами моей текущей области и моего вызывающего абонента не будет делать это и для меня, он не знает, что это нужно сделать. Поэтому добавьте его в свой пул, и как только вы очистите этот пул, также очистите мой объект для меня."

С Arc компилятор решает за вас, когда, чтобы сохранить объект, когда объект и когда, чтобы добавить его в autorelease пул, но он по-прежнему требует присутствия autorelease пулы, чтобы иметь возможность вернуться вновь созданные объекты из методов без утечки памяти. Apple только что сделала некоторые изящные оптимизацию генерируемого кода, которая иногда будет устранить autorelease бассейны во время выполнения. Эти оптимизации требуют, чтобы и вызывающий, и вызываемый использовали ARC (помните, что смешивание ARC и non-ARC является законным, а также официально поддерживается), и если это действительно так, может быть известно только во время выполнения.

рассмотрим этот код дуги:

// Callee
- (SomeObject *)getSomeObject {
    return [[SomeObject alloc] init];
}

// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];

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

// Callee
- (SomeObject *)getSomeObject {
    return [[[SomeObject alloc] init] autorelease];
}

// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];

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

или он может вести себя как этот код, в случае, если оба обнаруживаются для использования ARC во время выполнения:

// Callee
- (SomeObject *)getSomeObject {
    return [[SomeObject alloc] init];
}

// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];

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

теперь к актуальному вопросу: Зачем использовать @autoreleasepool?

для большинства разработчиков сегодня остается только одна причина для использования этой конструкции в своем коде, и это-сохранить небольшой объем памяти, где это применимо. Например, рассмотрим этот цикл:

for (int i = 0; i < 1000000; i++) {
    // ... code ...
    TempObject * to = [TempObject tempObjectForData:...];
    // ... do something with to ...
}

предположим, что каждый вызов tempObjectForData может создать новый TempObject что возвращается autorelease. For-loop создаст один миллион этих временных объектов, которые все собраны в текущем autoreleasepool и только после того, как этот пул будет уничтожен, все временные объекты также будут уничтожены. Пока это не произойдет, у вас есть миллион этих временных объектов в памяти.

если вы пишете такой код вместо этого:

for (int i = 0; i < 1000000; i++) @autoreleasepool {
    // ... code ...
    TempObject * to = [TempObject tempObjectForData:...];
    // ... do something with to ...
}

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

в прошлом вам часто приходилось также управлять autoreleasepools самостоятельно при управлении потоками (например, используя NSThread) как только основной поток автоматически имеет autorelease пул для какао/базе UIKit приложение. Тем не менее, это в значительной степени наследие сегодня, поскольку сегодня вы, вероятно, не будете использовать потоки для начала. Вы бы использовали GCD DispatchQueueили NSOperationQueueи эти двое как сделать управление верхнего уровня autorelease пул для вас создали перед запуском блока/задачи и разрушали покончим с этим.

там, кажется, много путаницы по этой теме (и по крайней мере 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

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

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

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

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