Як працює пул автовипуску 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 надсилається об'єкту, він додається до самого внутрішнього пулу автовипуску. Коли пул зливається, він просто надсилає -release до всіх об’єктів у басейні.

Пули автоматичного випуску - це просто зручність, яка дозволяє відкласти надсилання -release до "пізніше". Це "пізніше" може трапитися в декількох місцях, але найпоширенішим у програмах графічного інтерфейсу какао є кінець поточного циклу циклу запуску.


5
де кінець поточного циклу циклу запуску, якщо у мене немає циклу?
Дякую

24
Чи не повинно "зовнішнє-саме" бути "самим внутрішнім"?
Mike Weller

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допомагає системі збирати баланс.


4
Принципово неможливо "просочити" a NSAutoreleasePool. Це тому, що басейни працюють як стек. Екземпляр пулу штовхає цей пул до вершини потоку автовипуску стеку пулу. -releaseпризводить до того, що цей пул вискакує зі стеку І будь-які пули, які були насунуті на нього, але з будь-якої причини не з’явилися.
johne

7
Яким чином це стосується написаного мною?
mmalc

2
Мені подобається, як він знайшов час, щоб сміливо І. SNAP!
Billy Gray

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/…
johne

0

Що я читав від Apple: "Наприкінці блоку пулу автовипуску об’єктам, які отримали повідомлення про автоматичний випуск у блоці, надсилається повідомлення про звільнення - об’єкт отримує повідомлення про звільнення кожного разу, коли йому було надіслано повідомлення про автоматичний випуск у блоці. "

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


0

надсилання автовипуску замість звільнення об’єкту продовжує час життя цього об’єкта принаймні до спорожнення самого пулу (може бути довшим, якщо об’єкт згодом утримується). Об'єкт може бути поміщений в один і той же пул кілька разів, і в цьому випадку він отримує повідомлення про звільнення за кожен раз, коли він був поміщений в пул.


-2

Так і ні. В кінцевому підсумку ви випустили би рядок пам'яті, але "просочили" об'єкт NSAutoreleasePool в пам'ять, використовуючи drain замість звільнення, якщо ви запускали це в середовищі зібраного сміття (не керованого пам'яттю). Цей "витік" просто робить екземпляр NSAutoreleasePool "недосяжним", як і будь-який інший об'єкт без сильних покажчиків під GC, і об'єкт буде очищений під час наступного запуску GC, що цілком може бути безпосередньо після виклику -drain:

стік

У середовищі зібраного сміття запускає збір сміття, якщо пам’ять, виділена після останнього збору, перевищує поточний поріг; інакше поводиться як звільнення. ... У сміттєвому середовищі цей метод, нарешті, вимагає objc_collect_if_needed.

В іншому випадку це схоже на те, як -releaseповодиться за межами GC, так. Як зазначали інші, -releaseце заборона на використання в рамках GC, тому єдиний спосіб переконатися, що пул нормально функціонує під GC -drain, а -drainпід не-GC працює точно так само, як -releaseпід не-GC, і, можливо, передає свою функціональність більш чітко, як Ну.

Я повинен зазначити, що у вашому вислові "будь-що, що викликається за допомогою new, alloc або init" не повинно бути "init" (але повинно містити "copy"), оскільки "init" не виділяє пам'ять, він лише встановлює об'єкт (конструктор мода). Якщо ви отримали виділений об'єкт, а ваша функція викликала лише init як таку, ви не випустили б його:

- (void)func:(NSObject*)allocd_but_not_init
{
    [allocd_but_not_init init];
}

Це не споживає більше пам’яті, ніж ви вже почали (припускаючи, що init не створює екземпляри об’єктів, але ви все одно за них не відповідаєте).


Мені неприємно залишати цю відповідь прийнятою, коли ваша інформація про злив не зовсім правильна. Дивіться developer.apple.com/documentation/Cocoa/Reference/Foundation/… Оновлення, і я прийму його знову.
Джеймс Самнерс,

Що неточного у відповіді? У середовищі зібраного сміття (як зазначено) сток не видаляє AutoReleasePool, тому пам'ять буде втрачено, якщо ви не використовували випуск. Цитата, яку я перерахував, була прямо з пащі коня, документи на стоці.
Loren Segal

1
Лорен: Під GC, - [злив NSAutoreleasePool] ініціює збір. -retain, -release та -autorelease всі ігноруються колектором; ось чому -drain використовується на пулах автовипуску під GC.
Кріс Хенсон,

У документації для 'зливу': в середовищі керованої пам'яті це поводиться так само, як виклик випуску. Таким чином, ви не втратите пам'ять, якщо замість випуску використовуєте "злив".
mmalc

-[NSAutoreleasePool release]в оточенні сміття - це заборона. -[NSAutoreleasePool drain]працює як у середовищах, що підраховуються, так і в місцях, де збирається сміття.
Джонатан Стерлінг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.