Использование Autorelease пулов

Cocoa

Autorelease пулы обеспечивают механизм, посредством которого вы можете отправить объекту "отложенные" release сообщения. Это полезно в ситуациях, когда вы хотите отказаться от права владения объектом, но хотите избежать ситуации когда объект освобождается сразу (например, когда вы возвращаете объект из метода). Как правило, вам не нужно создавать свои собственные autorelease пулы, но есть некоторые ситуации, в которых либо вы должны, или это выгодно сделать.

Об Autorelease пулах

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

Autorelease пулы располагаются в стеке, хотя они обычно упоминаются как "вложенные". Если вы создаете новый autorelease пул, он добавляется к вершине стека. Когда пулы освобождаются, они удаляются из стека. Когда объекту передается autorelease сообщение, оно добавляется к текущей вершине пула для текущего потока.

Cocoa всегда ожидает, что autorelease пул доступен. Если пул не доступен, autoreleased объекты не освобождаются, и в вашем приложении происходит утечка памяти. Если вы отправляете autorelease сообщение, когда пул отсутствует, Cocoa сигнализирует соответствующим сообщением об ошибке. AppKit и UIKit framework-и автоматически создают пул в начале каждой итерации цикла событий, таких как события мыши или клавиатуры и сливает его в конце. Поэтому вам, как правило, не нужно создавать пул, или даже видеть код, который используется для его создания. Есть, однако три случая, когда вы можете использовать ваши собственные autorelease пулы:

  • Если вы пишете программу, которая не основывается на пользовательском интерфейсу, такая как инструмент командной строки.
  • Если вы пишете цикл, который создает много временных объектов.

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

  • Если вы порождаете вторичный поток.

    Вы должны создать свой собственный autorelease пул, как только поток начинает выполнение, в противном случае, в вашем приложении будет утечка объектов.

Вы создаете NSAutoreleasePool объект с обычным alloc и сообщением init, и вы распоряжаетесь его сливом (drain метод). За исключением, если вы посылаете autorelease или retain в autorelease пул. Чтобы понять разницу между выходом или стока, см. раздел “Garbage Collection.” мануала к Xcode. Autorelease пул всегда должен быть слит (drain) в том же контексте (например, вызов метода или функции, или тела цикла), в котором он был создан.

Autorelease пулы используются "встроенными". Надо взять за правило, что нет такой причины, почему вы должны сделать autorelease пул переменной экземпляра объекта.

Использование локального Autorelease пула для снижения пика потребности в оперативной памяти

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

Следующий пример показывает, как можно использовать локальные autorelease пулы в цикле.

NSArray *urls = <# Массив файлов URLs #>;

for (NSURL *url in urls) {

    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];

    NSError *error = nil;

    NSString *fileContents = [[[NSString alloc] initWithContentsOfURL:url
       encoding:NSUTF8StringEncoding error:&error] autorelease];

    /* создаются и автоуничтожаются множественные объекты. */

    [loopPool drain];
}

for цикл обрабатывает один файл за раз. Объект NSAutoreleasePool создан в начале этого цикла и осушен в конце. Таким образом, каждый объект послал сообщение autorelease внутри цикла (например, fileContents) добавляется к loopPool, и когда loopPool сливают в конце цикла эти объекты будут освобождены.

После того как autorelease пул будет освобожден, вы должны рассматривать любой объект, который был в то время как autoreleased и пул был активен, как "утилизированный". Не отправляйте сообщение на этот объект или верните его в вызывающий метод вашего метода. Если вы должны использовать временный объект за пределами autorelease контекста, вы можете сделать это, отправив retain сообщение для объекта в контексте, а затем отправить его autorelease после слива пула, как показано в следующем примере:

– (id)findMatchingObject:(id)anObject {

    id match = nil;

    while (match == nil) {

        NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];

  /* Выполните поиск, который создает много временных объектов. */

        match = [self expensiveSearchForObject:anObject];

        if (match != nil) {

            [match retain]; /* Keep match around. */

        }

        [subPool drain];

    }

    return [match autorelease];   /* Верните соответствующий match */
}

Отправляя retain к match в то время как подпул активен, и, отправив autorelease к нему после слива подпула , match фактически переехал из подпула в пул, который ранее был активен. Это увеличивает срок службы match и позволяет ему получать сообщения вне петли и возвращатся вызывающему методу метода findMatchingObject:.

Autorelease пулы и потоки

Каждый поток в Cocoa приложении сохраняет свой ​​собственный стек объектов NSAutoreleasePool. Когда поток завершается, он автоматически освобождает все autorelease пулы связаные с собой. Если вы пишете Foundation- приложение, или если вы отделяете поток, вам нужно создать свой собственный autorelease пул.

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

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

Объем Autorelease пулов и последствия вложенных Autorelease пулов

Это обобщенный разговор о вложенных autorelease пулах, но вы можете также думать о вложенных autorelease пулах как о стеке, с "внутренним" autorelease пулом находящемся на вершине стека. Каждый поток в программе поддерживает стек autorelease пулов. При создании autorelease пула, он помещается на вершину стека autorelease пула текущего потока. Когда объекту передается autorelease сообщение или когда он передается в качестве аргумента для метода AddObject: он всегда помещается в autorelease пул на вершину стека.

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

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

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

Сбор мусора

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

В среде сборки мусора, release холостая команда (ничего не делающая инструкция). NSAutoreleasePool, следовательно, обеспечивает drain метод, который в счетчиком ссылок среды ведет себя так же, как вызов release, но который в среде мусора инициирует сбор мусора (если памяти, выделенной с момента последней сборки больше текущего порога). Как правило, таким образом, вы должны использовать drain, а не release для распоряжения autorelease пулом.

 
 
homeЗаметили ошибкукарта сайта 
   Made on a Mac