Исчерпание памяти при создании UIImage в контексте Bitmap вне экрана с помощью NSOperation

У меня есть приложение с несколькими подклассами UIView, которое действует как страницы для UIScrollView. UIViews перемещаются вперед и назад, чтобы обеспечить беспроблемное взаимодействие с пользователем. Поскольку содержимое представлений отрисовывается довольно медленно, оно отображается на одном общем CGBitmapContext, охраняемом блокировками подклассами NSOperation - выполняется по одному сразу в NSOperationQueue - упаковывается в UIImage и затем используется основным потоком для обновления содержимого просмотров.

-(void)main {

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

if([self isCancelled]) {
    return;
}
if(nil == data) {
    return; 
}

// Buffer is the shared instance of a CG Bitmap Context wrapper class
// data is a dictionary
CGImageRef img = [buffer imageCreateWithData:data];
UIImage * image = [[UIImage alloc]initWithCGImage:img];
CGImageRelease(img);

if([self isCancelled]) {
    [image release];
    return;
}

NSDictionary * result = [[NSDictionary alloc]initWithObjectsAndKeys:image,@"image",id,@"id",nil];

// target is the instance of the UIView subclass that will use
// the image
[target performSelectorOnMainThread:@selector(updateContentWithData:) withObject:result waitUntilDone:NO];

[result release];
[image release];

[pool release];
}

UpdateContentWithData: подкласса UIView, выполняемого в основном потоке, так же просто

-(void)updateContentWithData:(NSDictionary *)someData {

NSDictionary * data = [someData retain];

if([[data valueForKey:@"id"]isEqualToString:[self pendingRequestId]]) {

    UIImage * image = [data valueForKey:@"image"];
    [self setCurrentImage:image];
    [self setNeedsDisplay];

}

// If the image has not been retained, it should be released together
// with the dictionary retaining it
[data release];
}

Метод drawLayer: inContext: подкласса просто получит CGImage из UIImage и использует его для обновления слоя поддержки или его части. В этом процессе не участвуют ни удержание, ни выпуск.

Проблема в том, что через некоторое время у меня заканчивается память. Количество UIViews статично. CGImageRef и UIImage создаются, сохраняются и выпускаются правильно (по крайней мере, мне так кажется). Инструменты не показывают утечек, просто объем доступной свободной памяти постоянно падает, несколько раз увеличивается, а затем падает еще ниже, пока приложение не будет завершено. Приложение циклически просматривает около 2-300 из вышеупомянутых страниц до этого, но я ожидал, что использование памяти достигнет более или менее стабильного уровня используемой памяти после того, как группа страниц уже была просмотрена на высокой скорости или, поскольку изображения имеют размер до 3 МБ, которые истощаются раньше.

Любое предложение будет принято с благодарностью.


person sigsegv    schedule 19.04.2010    source источник


Ответы (1)


Я понимаю, что это старая запись, но на случай, если она кому-то поможет .... Это похоже на фрагментацию памяти. У нас есть приложение, которое ведет себя точно так же. Объем памяти, фактически выделенной приложением, никогда не достигает опасного уровня, но если вы посмотрите на объем резидентной памяти для приложения (с помощью снимков VM Tracker в инструменте распределения или инструменте мониторинга активности), он будет неумолимо расти с течением времени до тех пор, пока не очень большой переходный всплеск убивает приложение.

Рассматриваемое приложение представляет собой многопоточное приложение, которое выполняет множество временных распределений в большом диапазоне размеров, время которых невозможно предсказать или контролировать. Такое приложение должно параноидально относиться к освобождению ненужных выделений памяти не потому, что они занимают слишком много памяти как таковые, а потому, что они могут создавать дыры, которые не позволяют помещать большие изображения в выделенные блоки. Даже меньшие выделения, которые обычно упускаются из виду, важны при фрагментации (при условии, что низкоуровневый распределитель группирует выделения по размеру, что в некоторой степени полезно). Зоны памяти теоретически полезны для устранения фрагментации, но их довольно сложно сделать эффективными, по крайней мере, по моему опыту. Кроме того, используйте настраиваемые пулы с автоматическим выпуском или, еще лучше, alloc / init как можно больше и выпускайте как можно раньше. Тот факт, что базовые фреймворки всегда выделяют свои собственные ресурсы для кэширования, вероятно, не помогает.

person Kevin    schedule 03.02.2011