Пулы Autorelease и когда релиз вызывается под iOS
Я хотел кое-что уточнить.
Допустим, у меня есть следующий код:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
for (int i = 0; i < 5000000; i++) {
NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
}
}
В этом вызове функции будет создано 5 миллионов строк с автореализацией. Я ожидал, что это сохранит эти объекты до окончания приложения, так как единственное @autoreleasepool, которое я вижу, - это тот, который обертывает экземпляр приложения в main.m. Впрочем, это не так. В конце вызова этой функции кажется, что все они вызывают их освобождение и удаляются из памяти.
Этот документ:
https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html
Сообщает, что "Application Kit создает пул автозапуска в основном потоке в начале каждого цикла цикла событий и истощает его в конце, тем самым высвобождая любые объекты с автореализацией, сгенерированные при обработке события".
Это имеет смысл для меня, но это под UIKit, а не Application Kit. Мой вопрос: делает ли UIKit/ Cocoa Touch то же самое в этом случае, или есть другое объяснение, что мои объекты освобождаются?
Спасибо!
Ответы
Ответ 1
Да, UIKit делает то же самое. Созданный системой пул авторесурсов основного потока сливается в конце каждого цикла цикла цикла. Вероятно, лучше не полагаться на эту точную жизнь в вашем собственном коде. Если вы создаете новый поток вручную (используя, например, NSThread), вы несете ответственность за создание пула автозапуска в этом потоке.
РЕДАКТИРОВАТЬ: Ответ Роба дает некоторую хорошую дополнительную информацию о поведении в ARC. В целом, справедливо сказать, что объекты с меньшей вероятностью попадают в пул авторезистов из-за некоторых оптимизаций, которые ARC может сделать.
Ответ 2
Эндрю ответил на ваш главный вопрос: да, ваш пул авторесурсов будет истощен в каждом цикле основного цикла запуска. Таким образом, любые объекты авторезиста, созданные в viewDidLoad
, могут быстро разрядиться, когда вы возвращаетесь к основному циклу запуска. Они, конечно же, не будут сохранены "до окончания подачи заявки".
Но мы должны быть осторожны: вы явно предполагаете, что эти объекты добавляются в пул автоопределений. Несколько предостережений к этому предположению:
-
В прошлом (и все еще требуемом для взаимодействия ARC-MRC) при возврате объектов из методов, имена которых не начинались с alloc
, new
, copy
или mutableCopy
, эти объекты будут объектами автоопределения, освобожденными только тогда, когда пул авторесурсов был истощен (т.е. когда вы вернулись в цикл выполнения).
-
Но ARC умнее сводит к минимуму необходимость в пулах автозапуска (см. http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease, в котором обсуждается callerAcceptsFastAutorelease
, теперь вызванный callerAcceptsOptimizedReturn
, вызываемый prepareOptimizedReturn
), поэтому вы часто не увидите этого поведения autorelease
. Таким образом, если и библиотека, и вызывающая сторона используют ARC, объекты не могут быть помещены в пул автозаполнения, но, скорее всего, ARC умственно освободит их немедленно, если они не понадобятся.
В современных проектах ARC пулы авторефератов обычно не нужны. Но в некоторых особых случаях все равно можно использовать пулы авторефератов. Я расскажу об одном из следующих случаев.
Рассмотрим следующий код:
#import "ViewController.h"
#import <sys/kdebug_signpost.h>
typedef enum : NSUInteger {
InnerLoop = 1,
MainLoop = 2
} MySignPostCodes;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
for (int j = 0; j < 500; i++) {
NSData *data = [NSData dataWithContentsOfURL:fileURL];
UIImage *image = [[UIImage alloc] initWithData:data];
NSLog(@"%p", NSStringFromCGSize(image.size)); // so it not optimized out
[NSThread sleepForTimeInterval:0.01];
}
kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
});
}
@end
Следующий код добавит 500 000 объектов в пул автозаполнения, который будет только слит, когда я вернусь к циклу запуска:
![no pool]()
В этом случае вы можете использовать пул авторесурсов, чтобы свести к минимуму знак высокой воды:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
for (int j = 0; j < 5; j++) {
@autoreleasepool {
kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
for (long i = 0; i < 100; i++) {
NSData *data = [NSData dataWithContentsOfURL:fileURL];
UIImage *image = [[UIImage alloc] initWithData:data];
NSLog(@"%p", NSStringFromCGSize(image.size)); // so it not optimized out
[NSThread sleepForTimeInterval:0.01];
}
kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
}
}
kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
});
}
@end
![pool]()
В нижней строке, с ARC, это не всегда очевидно, когда он использует объект автоопределения и когда он явно освобождает его, когда переменная выходит из области видимости. Вы всегда можете подтвердить это, исследуя поведение в Инструментах.
В стороне я бы с осторожностью относился к выработке слишком большого количества решений управления памятью при использовании класса NSString
, так как он был высоко оптимизирован и не всегда соответствовал стандартным методам управления памятью.
Ответ 3
Я бы предположил, что когда вы назначаете новый объект ссылке, используемой для хранения объекта, тогда исходный объект сразу отпускается (если на него ничего не указывает - количество ссылок обращается в ноль) используя ARC и присваивая ссылку по умолчанию strong
как в примере вашего цикла.
MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
// so obj1 gets released
Apple отмечает в Переход к заметкам о выпуске ARC
Попытайтесь перестать думать о том, куда помещаются вызовы сохранения/выпуска, и подумайте о своих алгоритмах применения. Подумайте о "сильных и слабых" указателях в ваших объектах, об объектной собственности и о возможных циклах сохранения.
Звучит так, что release
вызывается на объект, когда ему присваивается новое значение,
от clang Документация Clang 3.4
OBJECTIVE-C АВТОМАТИЧЕСКИЙ СЧЕТЧИК (ARC)
Присвоение выполняется при оценке оператора присваивания. семантика варьируется в зависимости от квалификации:
Для объектов __strong новый указатель сначала сохраняется; во-вторых, lvalue загружается примитивной семантикой; в-третьих, новый хранится в lvalue с примитивной семантикой; и, наконец, старый pointee выпущен. Это не выполняется атомарно; внешний синхронизация должна использоваться, чтобы сделать это безопасным в одновременные нагрузки и магазины.