2009-05-29 13 views
116

Editar Feb 2014: ¡Tenga en cuenta que esta pregunta data de iOS 2.0! Los requisitos de imagen y el manejo se han movido mucho desde entonces. Retina hace las imágenes más grandes y las carga un poco más complejas. Con el soporte integrado para imágenes de iPad y retina, , debe utilizar ImageNamed en su código.Disipando el UIImage imageNamed: FUD

Veo a mucha gente diciendo que imageNamed es malo pero hay un número igual de personas que dicen que el rendimiento es bueno, especialmente cuando se procesa UITableView s. Ver this SO question por ejemplo, o en this article iPhoneDeveloperTips.com

UIImage 's imageNamed método utilizado para filtrar por lo que fue mejor evitar, pero se ha corregido en las últimas versiones. Me gustaría entender mejor el algoritmo de almacenamiento en caché para tomar una decisión razonada sobre dónde puedo confiar en que el sistema almacenará en caché mis imágenes y dónde tengo que hacer un esfuerzo adicional y hacerlo yo mismo. Mi entendimiento básico actual es que es un simple NSMutableDictionary de UIImages al que se hace referencia por nombre de archivo. Se hace más grande y cuando la memoria se agota se vuelve mucho más pequeña.

Por ejemplo, ¿alguien sabe con certeza que la memoria caché de imágenes detrás de imageNamed no responde a didReceiveMemoryWarning? Parece poco probable que Apple no haga esto.

Si tiene alguna idea sobre el algoritmo de almacenamiento en caché, publíquelo aquí.

+1

Estoy en suspenso. :) – Kriem

+2

yo también. Parece que SO no está tan lleno de genios como yo pensaba. –

+0

Parece que has pasado algún tiempo investigando esto. ¿Has hecho algún experimento que muestre algún impacto negativo de UIImage imageNamed con la última versión de cocoa-touch? Cree una UITableView y agregue muchas (muchas) filas y vea si el rendimiento disminuye cuando muestra imágenes diferentes en cada fila. Quizás entonces las personas puedan comentar sus hallazgos. – stefanB

Respuesta

86

tldr: ImagedNamed está bien. Maneja bien la memoria. Úselo y deja de preocuparte.

Editar Nov 2012: Tenga en cuenta que esta pregunta data de iOS 2.0! Los requisitos de imagen y manejo han avanzado mucho desde entonces. Retina hace las imágenes más grandes y las carga un poco más complejas. Con el soporte incorporado para imágenes de iPad y retina, seguramente deberías usar ImageNamed en tu código. Ahora, por la posteridad:

El sister thread en los foros de Apple Dev recibió algo de tráfico mejor. Específicamente Rincewind añadió algo de autoridad.

Hay problemas en el iPhone OS 2.x donde el caché imageNamed: no se borró, incluso después de una advertencia de memoria. Al mismo tiempo, + imageNamed: se ha utilizado mucho no para la memoria caché, sino para mayor comodidad, lo que probablemente ha magnificado el problema más de lo que debería haber sido.

aunque advierte de que

En el frente de la velocidad, hay un malentendido general de lo que está pasando. Lo más importante que + imageNamed: hace es decodificar los datos de imagen del archivo fuente, lo que casi siempre infla significativamente el tamaño de los datos (por ejemplo, un archivo PNG con tamaño de pantalla podría consumir unas pocas docenas de KB cuando está comprimido, pero consume más de medio MB descomprimido - ancho * alto * 4). Por contraste + imageWithContentsOfFile: descomprimirá esa imagen cada vez que se necesiten los datos de la imagen. Como puedes imaginar, si solo necesitas los datos de la imagen una vez, no has ganado nada aquí, excepto para tener una versión almacenada en caché de la imagen, y probablemente por más tiempo del que necesites. Sin embargo, si tiene una imagen grande que necesita volver a dibujar a menudo, existen alternativas, aunque la que recomiendo principalmente es evitar volver a dibujar esa imagen grande :).

Con respecto al comportamiento general de la memoria caché, la memoria caché se basa en el nombre de archivo (por lo que dos instancias de + imageNamed: con el mismo nombre deberían hacer referencia a los mismos datos en caché) y la memoria caché crecerá dinámicamente a medida solicitar más imágenes a través de + imageNamed :. En iPhone OS 2.x, un error impide que la memoria caché se reduzca cuando se recibe una advertencia de memoria.

y

Mi entendimiento es que el imageNamed +: caché debe respetar las advertencias de memoria en el iPhone OS 3.0. Pruébelo cuando tenga la oportunidad e informe de errores si encuentra que este no es el caso.

Así que, ahí lo tienes. imageNamed: no destruirá tus ventanas ni asesinará a tus hijos. Es bastante simple pero es una herramienta de optimización. Lamentablemente está mal nombrado y no hay equivaluent que es tan fácil de usar - por lo tanto la gente utilizarlo en exceso y se molestan cuando simplemente hace su trabajo

he añadido una categoría para UIImage de arreglar eso:

// 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 también incluyó algunos códigos de ejemplo para construir su propia versión optimizada. No puedo ver que valga la pena el maintentace, pero aquí está para completar.

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

La compensación con este código es que la imagen decodificada usa más memoria, pero el procesamiento es más rápido.

+0

Parece que en iOS11, [UIImage imageNamed:] no está desalojando las imágenes de la memoria caché si las imágenes provienen del catálogo xcasset. Esto conduce a bloqueos debido a la falta de memoria. –

5

En mi experiencia, la memoria caché de imágenes creada por imageNamed no responde a las advertencias de memoria. He tenido dos aplicaciones que eran tan delgadas como podía conseguirlas en cuanto a la administración de la memoria, pero seguían inexplicablemente bloqueadas debido a la falta de memoria. Cuando dejé de usar imageNamed para cargar las imágenes, ambas aplicaciones se volvieron dramáticamente más estables.

Admitiré que ambas aplicaciones cargaron imágenes algo grandes, pero nada que fuera totalmente fuera de lo normal. En la primera aplicación, me salté el almacenamiento en caché por completo porque era poco probable que un usuario volviera a la misma imagen dos veces. En el segundo, construí una clase de almacenamiento en caché muy simple haciendo exactamente lo que usted mencionó: mantener UIImages en un NSMutableDictionary y luego descargar su contenido si recibí una advertencia de memoria. Si imageNamed: caché así, entonces no debería haber visto ninguna actualización de rendimiento. Todo esto se estaba ejecutando en 2.2 - No sé si hay implicaciones de 3.0 en esto.

puede encontrar mi otra pregunta en torno a este tema de mi primera aplicación aquí: StackOverflow question about UIImage cacheing

Otra nota - InterfaceBuilder utiliza imageNamed bajo las sábanas. Algo a tener en cuenta si se encuentra con este problema.

+0

Vi exactamente el mismo comportamiento y la lectura del archivo lo resolvió directamente. Además, sucede cuando intento cargar una imagen muy grande: 11,456 x 3,226 px, 15MB. Mi teoría es que el sistema se queda sin memoria en parte a través de la operación de caché ImageNamed. Y no hay manejo integrado en este caso. – Dogweather

Cuestiones relacionadas