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


Edit Feb 2014:обратите внимание, что этот вопрос относится к iOS 2.0! требования к изображению и обработка сильно продвинулись с тех пор. Retina делает изображения больше и загружает их немного сложнее. С встроенной поддержкой iPad и retina изображений,вы обязательно должны использовать ImageNamed в своем коде.

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

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

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

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

2 117

2 ответа:

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

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

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

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

пока предупреждение это

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

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

и

Я понимаю, что +imageNamed: cache должен уважать предупреждения памяти на 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 также включил некоторые примеры кода для создания собственной оптимизированной версии. Я не вижу, что это стоит maintentace, но вот для полноты картины.

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

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

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

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

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

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