Разогнать UIImage imageNamed: FUD - PullRequest
117 голосов
/ 29 мая 2009

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

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

Метод

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

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

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

Ответы [ 2 ]

86 голосов
/ 02 июня 2009

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

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

сестринская ветка на форумах Apple Dev получила немного лучшего трафика. В частности Rincewind добавил некоторые полномочия.

В 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]];
}

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

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);

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

5 голосов
/ 02 июня 2009

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

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

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

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

...