Рассеивание изображения UIImage Имя: FUD

Изменить февраль 2014 г .: Обратите внимание, что этот вопрос относится к iOS 2.0! С тех пор требования к изображениям и их обработка значительно изменились. Retina увеличивает размер изображений и немного усложняет их загрузку. Благодаря встроенной поддержке изображений iPad и Retina, вам непременно следует использовать ImageNamed в своем коде.

Я вижу, как много людей говорят, что imageNamed плохо, но такое же количество людей говорят, что производительность хорошая, особенно при рендеринге UITableViews. См., Например, этот вопрос SO или эта статья на iPhoneDeveloperTips.com

Метод imageNamed UIImage использовался для утечки, поэтому его лучше избегать, но он был исправлен в последних выпусках. Я хотел бы лучше понять алгоритм кеширования, чтобы принять обоснованное решение о том, где я могу доверить системе кэширование моих изображений, а где мне нужно сделать все возможное и сделать это самому. В настоящее время я понимаю, что это простой NSMutableDictionary из UIImages, на который ссылается имя файла. Он становится больше, а когда заканчивается память, становится намного меньше.

Например, кто-нибудь знает наверняка, что кеш изображений за imageNamed не отвечает на didReceiveMemoryWarning? Кажется маловероятным, что Apple этого не сделает.

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


person Rog    schedule 29.05.2009    source источник
comment
я тоже. Похоже, в SO не так много гениев, как я думал.   -  person Rog    schedule 31.05.2009
comment
Похоже, вы потратили некоторое время на изучение этого. Проводили ли вы какие-либо эксперименты, которые показали бы какое-либо негативное влияние UIImage imageNamed с последней версией какао-touch? Создайте UITableView и добавьте много (много тысяч) строк и посмотрите, не снизится ли производительность, когда вы показываете разные изображения в каждой строке. Может быть, тогда люди смогут прокомментировать ваши выводы.   -  person stefanB    schedule 01.06.2009
comment
На самом деле, я не проводил так много экспериментов. Я использую imageNamed, и хотя я еще не оптимизировал это приложение для памяти, у меня нет ничего, что я мог бы указать прямо на imageNamed как на пожиратель памяти. Я указал здесь на несколько сообщений в блогах, в которых жалуются на imagedNamed, и я задал аналогичный вопрос на досках Apple, где было больше действий. Я перепечатаю резюме и сделаю ссылку здесь, когда у меня будет время и я буду чувствовать, что он уже получил ответы, которые он собирается получить.   -  person Rog    schedule 01.06.2009


Ответы (2)


tldr: ImagedNamed в порядке. Он хорошо обрабатывает память. Используйте это и перестаньте беспокоиться.

Изменить ноябрь 2012 г.: обратите внимание, что этот вопрос относится к iOS 2.0! С тех пор требования к изображениям и их обработка сильно изменились. Retina увеличивает размер изображений и немного усложняет их загрузку. Благодаря встроенной поддержке изображений iPad и Retina вам непременно следует использовать ImageNamed в своем коде. А теперь для потомков:

родственная ветка на форумах разработчиков Apple привлекла больше внимания. В частности, Ринсвинд добавил некоторые полномочия.

В iPhone OS 2.x есть проблемы, из-за которых кеш imageNamed: не очищается даже после предупреждения о памяти. В то же время + imageNamed: широко используется не для кеширования, а для удобства, что, вероятно, усугубило проблему больше, чем должно было быть.

предупреждая, что

Что касается скорости, существует общее непонимание происходящего. Самая большая вещь, которую делает + imageNamed: - это декодирование данных изображения из исходного файла, что почти всегда значительно увеличивает размер данных (например, файл PNG размером с экран может потреблять несколько десятков КБ при сжатии, но занимает более половины МБ. в распакованном виде - ширина * высота * 4). Напротив, + imageWithContentsOfFile: будет распаковывать это изображение каждый раз, когда требуются данные изображения. Как вы понимаете, если вам нужны данные изображения только один раз, вы ничего здесь не выиграете, кроме того, что у вас будет храниться кешированная версия изображения, и, вероятно, дольше, чем вам нужно. Однако, если у вас есть большое изображение, которое вам нужно часто перерисовывать, тогда есть альтернативы, хотя я бы рекомендовал в первую очередь избегать перерисовки этого большого изображения :).

Что касается общего поведения кеша, он выполняет кеширование на основе имени файла (поэтому два экземпляра + imageNamed: с тем же именем должны приводить к ссылкам на одни и те же кэшированные данные), и кеш будет динамически расти по мере того, как вы запрашиваете больше изображений через + imageNamed :. В iPhone OS 2.x ошибка предотвращает сжатие кеша при получении предупреждения о памяти.

и

Насколько я понимаю, кеш + imageNamed: должен учитывать предупреждения о памяти на iPhone OS 3.0. Проверьте это, когда у вас будет возможность, и сообщите об ошибках, если вы обнаружите, что это не так.

Итак, вот оно. imageNamed: не будет разбивать ваши окна или убивать ваших детей. Это довольно просто, но это инструмент оптимизации. К сожалению, он плохо назван, и нет эквивалента, который был бы столь же прост в использовании, поэтому люди злоупотребляют им и расстраиваются, когда он просто выполняет свою работу.

Я добавил категорию в UIImage, чтобы исправить это:

// header omitted
// Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style.
+ (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; {
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
    return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}

Ринсвинд также включил несколько примеров кода для создания вашей собственной оптимизированной версии. Я не вижу, чтобы оно того стоило, но здесь оно для полноты картины.

CGImageRef originalImage = uiImage.CGImage;
CFDataRef imageData = CGDataProviderCopyData(
     CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
     CGImageGetWidth(originalImage),
     CGImageGetHeight(originalImage),
     CGImageGetBitsPerComponent(originalImage),
     CGImageGetBitsPerPixel(originalImage),
     CGImageGetBytesPerRow(originalImage),
     CGImageGetColorSpace(originalImage),
     CGImageGetBitmapInfo(originalImage),
     imageDataProvider,
     CGImageGetDecode(originalImage),
     CGImageGetShouldInterpolate(originalImage),
     CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *decompressedImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);

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

person Rog    schedule 02.06.2009
comment
Похоже, что в iOS11 [UIImage imageNamed:] не удаляет изображения из кеша, если изображения поступают из каталога xcasset. Это приводит к сбоям из-за нехватки памяти. - person Juraj Antas; 06.02.2018

По моему опыту, кеш изображений, созданный imageNamed, не реагирует на предупреждения памяти. У меня было два приложения, которые были настолько скудными, насколько я мог их довести до управления памятью, но все равно по необъяснимым причинам вылетали из строя из-за нехватки памяти. Когда я перестал использовать imageNamed для загрузки изображений, оба приложения стали значительно более стабильными.

Я признаю, что оба приложения загружали довольно большие изображения, но ничего необычного. В первом приложении я просто полностью пропустил кеширование, потому что маловероятно, что пользователь вернется к одному и тому же изображению дважды. Во втором случае я создал действительно простой класс кэширования, который делает именно то, что вы упомянули, - хранит UIImages в NSMutableDictionary и затем сбрасывает его содержимое, если я получил предупреждение о памяти. Если бы imageNamed: кешировали таким образом, я бы не увидел никакого повышения производительности. Все это работало на 2.2 - я не знаю, есть ли какие-то последствия для 3.0.

Другой мой вопрос по этой проблеме из моего первого приложения вы можете найти здесь: вопрос StackOverflow о кешировании UIImage

Еще одно замечание - InterfaceBuilder использует imageNamed под покровом. О чем следует помнить, если вы столкнетесь с этой проблемой.

person Bdebeez    schedule 02.06.2009
comment
Я видел точно такое же поведение, и чтение из файла сразу разрешило его. Кроме того, это происходит, когда я пытаюсь загрузить очень большое изображение - 11 456 x 3226 пикселей, 15 МБ. Моя теория заключается в том, что системе не хватает памяти на полпути кэширования ImageNamed. И в этом случае нет встроенной обработки. - person Dogweather; 12.04.2013