2010-09-21 7 views
6

Estoy tratando de mejorar el rendimiento de mi aplicación para iPhone con imágenes intensas mediante el uso de una memoria caché de imágenes basada en disco en lugar de pasar por la red. He modelado mi caché de imágenes después de SDImageCache (http://github.com/rs/SDWebImage/blob/master/SDImageCache.m), y es prácticamente lo mismo, pero sin operaciones de entrada/salida de caché asíncronas.Vista de desplazamiento y rendimiento de la vista de tabla al cargar imágenes desde el disco

Tengo algunas vistas de desplazamiento y vistas de tabla que cargan estas imágenes de forma asíncrona. Si la imagen está en el disco, se carga desde la memoria caché de imágenes; de lo contrario, se realiza una solicitud de red y el resultado posterior se almacena en la caché.

El problema al que me estoy enfrentando es que a medida que me desplazo por las vistas de desplazamiento o las vistas de tabla, hay un retraso notable a medida que la imagen se carga desde el disco. En particular, la animación de pasar de una página a otra en una vista de desplazamiento tiene una pequeña congelación en el medio de la transición.

he tratado de solucionar este problema:

  • Usando un NSOperationQueue y NSInvocationOperation objetos para hacer las solicitudes de acceso a disco (de la misma manera que SDImageCache), pero no resuelve el problema con el retraso en el todas.
  • Afinando el código del controlador de vista de desplazamiento para que solo cargue imágenes cuando la vista de desplazamiento ya no se desplaza. Esto significa que el acceso al disco solo se dispara cuando la vista de desplazamiento deja de desplazarse, pero si inmediatamente trato de desplazarme a la siguiente página, noto el desfase a medida que la imagen se carga desde el disco.

¿Hay alguna manera de que los accesos a mi disco rindan mejor o tengan un efecto menor en la IU?

Tenga en cuenta que ya estoy almacenando en caché las imágenes en la memoria también. Entonces, una vez que todo está cargado en la memoria, la interfaz de usuario es agradable y receptiva. Pero cuando la aplicación se inicia o si se envían advertencias de memoria baja, experimentaré muchos de estos retrasos de UI a medida que las imágenes se cargan desde el disco.

Los fragmentos de código relevantes están a continuación. No creo que esté haciendo nada elegante o loco. El retraso no parece ser notable en un iPhone 3G, pero es bastante evidente en un iPod Touch de 2da generación.

Imagen Código de almacenamiento en caché:

He aquí un fragmento relevante de mi código de la imagen de almacenamiento en caché. Muy claro.

- (BOOL)hasImageDataForURL:(NSString *)url { 
    return [[NSFileManager defaultManager] fileExistsAtPath:[self cacheFilePathForURL:url]]; 
} 

- (NSData *)imageDataForURL:(NSString *)url { 
    NSString *filePath = [self cacheFilePathForURL:url]; 

    // Set file last modification date to help enforce LRU caching policy 
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; 
    [attributes setObject:[NSDate date] forKey:NSFileModificationDate]; 
    [[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:filePath error:NULL]; 

    return [NSData dataWithContentsOfFile:filePath]; 
} 

- (void)storeImageData:(NSData *)data forURL:(NSString *)url { 
    [[NSFileManager defaultManager] createFileAtPath:[self cacheFilePathForURL:url] contents:data attributes:nil]; 
} 

Scroll código de controlador de vista

He aquí un fragmento correspondiente del código que utilizo para la visualización de imágenes en mis controladores de vista de desplazamiento.

- (void)scrollViewDidScroll:(UIScrollView *)theScrollView { 
    CGFloat pageWidth = theScrollView.frame.size.width; 
    NSUInteger index = floor((theScrollView.contentOffset.x - pageWidth/2)/pageWidth) + 1; 

    [self loadImageFor:[NSNumber numberWithInt:index]]; 
    [self loadImageFor:[NSNumber numberWithInt:index + 1]]; 
    [self loadImageFor:[NSNumber numberWithInt:index - 1]]; 
} 

- (void)loadImageFor:(NSNumber *)index { 
    if ([index intValue] < 0 || [index intValue] >= [self.photoData count]) { 
     return; 
    } 

    // ... initialize an image loader object that accesses the disk image cache or makes a network request 

    UIView *iew = [self.views objectForKey:index];  
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag]; 
    if (imageView.image == nil) { 
     NSDictionary *photo = [self.photoData objectAtIndex:[index intValue]]; 
     [loader loadImage:[photo objectForKey:@"url"]]; 
    } 
} 

El objeto cargador imagen es sólo un objeto ligero que comprueba la caché de disco y decide si o no para ir a buscar una imagen de disco o red.Una vez hecho esto, se llama a un método en el controlador de vista de desplazamiento para mostrar la imagen:

- (void)imageLoadedFor:(NSNumber *)index image:(UIImage *)image { 
    // Cache image in memory 
    // ... 

    UIView *view = [self.views objectForKey:index]; 
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag]; 
    imageView.contentMode = UIViewContentModeScaleAspectFill; 
    imageView.image = image; 
} 

ACTUALIZACIÓN

Estaba experimentando con la aplicación, y he deshabilitado la caché de imágenes y volvió siempre a hacer solicitudes de red. Parece que el simple uso de solicitudes de red para buscar imágenes es , lo que también provoca el mismo retraso al desplazarse por las vistas de desplazamiento y las vistas de tabla. Es decir, cuando una solicitud de red finaliza y la imagen se muestra en la página de vista de desplazamiento o en la celda de la tabla, la IU se retrasa ligeramente un poco y tiene unos pocos segundos de retraso cuando trato de arrastrarlo.

El retraso parece ser más notable cuando se utiliza la memoria caché de disco, ya que el retraso siempre ocurre justo en la transición de la página. ¿Quizás estoy haciendo algo mal al asignar la imagen cargada al UIImageView apropiado?

Además, he intentado usar imágenes pequeñas (miniaturas de 50x50) y el desfase parece mejorar. Por lo tanto, parece que el rendimiento alcanzado se debe a la carga de una imagen grande desde el disco o la carga de una imagen grande en un objeto UIImage. Creo que una mejora sería reducir el tamaño de las imágenes que se cargan en la vista de desplazamiento y las vistas de tabla, que era lo que estaba planeando hacer de todos modos. Sin embargo, simplemente no entiendo cómo otras aplicaciones intensivas de fotos pueden presentar lo que parecen ser fotos de alta resolución en vistas desplazables sin problemas de rendimiento al ir al disco oa través de la red.

+0

Tenemos el mismo problema en iPad con imágenes grandes. Le avisaré si encontramos alguna solución –

+0

Una de las claves principales para el desplazamiento continuo es nunca bloquear el hilo principal. El hilo principal es el hilo de UI, que tiene que gestionar las altas actualizaciones de framerate a la pantalla y las devoluciones de llamada de eventos asociadas a su controlador. Sin ver su código NSUURLConnection, no puedo decir si lo está haciendo en un hilo separado o no, sin embargo, esta sería mi primera corazonada. ¿Puedes publicar tu código de manejo de red? – Yetanotherjosh

+0

compruebe el proyecto TCImageView github. Tienen una implementación bastante ordenada para la carga lenta de imágenes y el almacenamiento en caché del disco https://github.com/totocaster/TCImageView/blob/master/TCImageView.m –

Respuesta

0

La imagen del disco se lee realmente mientras se dibuja la imagen en la vista de la imagen. Incluso si almacenamos en caché la lectura de la imagen desde el disco, no afecta, ya que solo hace referencia al archivo. Es posible que deba usar mosaico de imágenes más grandes para este propósito.

Saludos, Deepa

1

Debe retirar la aplicación LazyTableImages muestra.

1

Si lo has reducido a la actividad de red, trataría de encapsular tu solicitud para asegurarte de que esté 100% fuera del hilo principal. Si bien puede usar NSURLConnection de forma asíncrona y responder a sus métodos delegados, me resulta más fácil ajustar una solicitud síncrona en una operación en segundo plano. Puede usar NSOperation o grand central dispatch si sus necesidades son más complejas. Un (relativamente) simple ejemplo de una implementación imageLoader podría ser:

// imageLoader.m 

// assumes that that imageCache uses kvp to look for images 
- (UIImage *)imageForKey:(NSString *)key 
{ 
    // check if we already have the image in memory 
    UImage *image = [_images objectForKey:key]; 

    // if we don't have an image: 
    // 1) start a background task to load an image from a file or URL 
    // 2) return a default image to display while loading 
    if (!image) { 
     [self performSelectorInBackground:@selector(loadImageForKey) withObject:key]; 
     image = [self defaultImage]; 
    } 

    return image; 
} 

- (void)loadImageForKey:(NSString *)key 
{ 
    NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init]; 

    // attempt to load the image from the file cache 
    UIImage *image = [self imageFromFileForKey:key]; 

    // if no image, load the image from the URL 
    if (!image) { 
     image = [self imageFromURLForKey:key]; 
    } 

    // if no image, return default or imageNotFound image 
    if (!image) { 
     image = [self notFoundImage]; 
    } 

    if ([_delegate respondsTo:@selector(imageLoader:didLoadImage:ForKey:)]) { 
     [_delegate imageLoader:self didLoadImage:image forKey:key]; 
    } 

    [pool release]; 
} 

- (UIImage *)imageFromURLForKey:(NSString *)key 
{ 
    NSError *error = nil; 
    NSData *imageData = [NSData dataWithContentsOfURL:[self imageURLForKey:key] 
               options:0 
               error:&error]; 

    UIImage *image; 

    // handle error if necessary 
    if (error) { 
     image = [self errorImage]; 
    } 

    // create image from data 
    else { 
     image = [UIImage imageWithData:imageData]; 
    } 

    return image; 
} 
0

que he tenido este problema - usted está golpeando el límite de la rapidez con la interfaz de usuario puede cargar una imagen mientras se desplaza - por lo que acababa de trabajo alrededor del problema y mejorar la experiencia del usuario.

  1. cargar sólo las imágenes cuando el rollo está en una nueva 'página' (rectángulo estática) y
  2. Poner un indicador de actividad detrás de una vista de desplazamiento transparente para manejar el caso en que el usuario se desplaza más rápido que la aplicación puede cargar contenido
0

Normalmente es la decodificación de imágenes lo que lleva tiempo y hace que la IU se congele (ya que todo está sucediendo en el hilo principal). Cada vez que llamas al [UIImage imageWithData:] en una imagen grande, notarás un contratiempo. La decodificación de una imagen más pequeña es mucho más rápida.

dos opciones:

  1. Se puede cargar una versión en miniatura de cada imagen en primer lugar, a continuación, 'afinar' en didFinishScrolling. Las miniaturas deberían decodificarse lo suficientemente rápido como para que no salte ningún marco.
  2. Puede cargar una versión en miniatura de cada imagen o mostrar primero un indicador de carga, luego decodificar la imagen de resolución completa en otro subproceso y subincluirla cuando esté lista. (Esta es la técnica que se emplea en la aplicación de fotos nativa).

Prefiero el segundo enfoque; es bastante fácil con GDC hoy en día.

Cuestiones relacionadas