Рассеивание UIImage imageNamed: FUD

Ваш текст имеет кодировку ASCII-8BIT, вместо этого вы должны использовать это:

String.delete!("^\u{0000}-\u{007F}"); 

Он будет служить той же цели.

117
задан Community 23 May 2017 в 11:46
поделиться

2 ответа

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

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

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

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

при этом предупреждает, что

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

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

и

Насколько я понимаю, кеш + 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);

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

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

Я добавил категорию в 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);

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

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

Я добавил категорию в 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);

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

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

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

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

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

86
ответ дан 24 November 2019 в 02:09
поделиться

In my experience, the image cache created by imageNamed does not respond to memory warnings. I've had two applications that were as lean as I could get them as far as mem management, but were still inexplicably crashing due to lack of mem. When I stopped using imageNamed to load the images, both applications became dramatically more stable.

I will admit that both applications loaded somewhat large images, but nothing that would be totally out of the ordinary. In the first application, I just skipped caching altogether because it was unlikely a user would come back to the same image twice. In the second, I built a really simple caching class doing just what you mentioned - keeping UIImages in an NSMutableDictionary and then flushing its contents if I received a memory warning. If imageNamed: were to cache like that, then I shouldn't have seen any performance upgrade. All of this was running on 2.2 - I don't know if there's any 3.0 implications on this.

You can find my other question around this issue from my first app here: Вопрос StackOverflow о кешировании UIImage

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

5
ответ дан 24 November 2019 в 02:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: