2009-07-08 12 views
16

Tengo un UIScrollView que tiene un conjunto de imágenes cargadas una al lado de la otra. Puede ver un ejemplo de mi aplicación aquí: http://www.42restaurants.com. Mi problema entra con el uso de la memoria. Quiero cargar las imágenes perezosas cuando están a punto de aparecer en la pantalla y descargar las imágenes que no están en la pantalla. Como puede ver en el código, resuelvo, como mínimo, qué imagen necesito cargar y luego asigno la porción de carga a una operación NSO y la coloco en un NSOperationQueue. Todo funciona muy bien, aparte de una experiencia de desplazamiento desigual.Carga de imagen optimizada en un UIScrollView

No sé si alguien tiene alguna idea de cómo puedo optimizar aún más esto, de modo que el tiempo de carga de cada imagen se minimice o el desplazamiento sea menos espasmódico.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 
    [self manageThumbs];  
} 

- (void) manageThumbs{ 
    int centerIndex = [self centerThumbIndex]; 
    if(lastCenterIndex == centerIndex){ 
     return; 
    } 

    if(centerIndex >= totalThumbs){ 
     return; 
    } 

    NSRange unloadRange; 
    NSRange loadRange; 

    int totalChange = lastCenterIndex - centerIndex; 
    if(totalChange > 0){ //scrolling backwards 
     loadRange.length = fabsf(totalChange); 
     loadRange.location = centerIndex - 5; 
     unloadRange.length = fabsf(totalChange); 
     unloadRange.location = centerIndex + 6; 
    }else if(totalChange < 0){ //scrolling forwards 
     unloadRange.length = fabsf(totalChange); 
     unloadRange.location = centerIndex - 6; 
     loadRange.length = fabsf(totalChange); 
     loadRange.location = centerIndex + 5; 
    } 
    [self unloadImages:unloadRange]; 
    [self loadImages:loadRange]; 
    lastCenterIndex = centerIndex; 

    return; 
} 

- (void) unloadImages:(NSRange)range{ 
    UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0]; 
    for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){ 
     UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)]; 
     if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){ 
      ThumbnailView *thumbView = (ThumbnailView *)subview; 
      if(thumbView.loaded){ 
       UnloadImageOperation *unloadOperation = [[UnloadImageOperation alloc] initWithOperableImage:thumbView]; 
       [queue addOperation:unloadOperation]; 
       [unloadOperation release]; 
      } 
     } 
    } 
} 

- (void) loadImages:(NSRange)range{ 
    UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0]; 
    for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){ 
     UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)]; 
     if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){ 
      ThumbnailView *thumbView = (ThumbnailView *)subview; 
      if(!thumbView.loaded){ 
       LoadImageOperation *loadOperation = [[LoadImageOperation alloc] initWithOperableImage:thumbView]; 
       [queue addOperation:loadOperation]; 
       [loadOperation release]; 
      } 
     } 
    } 
} 

EDIT: Gracias por las respuestas muy grandes. Aquí está mi código NSOperation y el código ThumbnailView. Intenté un par de cosas durante el fin de semana, pero solo pude mejorar el rendimiento al suspender la cola de operaciones durante el desplazamiento y reanudarla cuando finaliza el desplazamiento.

Éstos son mis fragmentos de código:

//In the init method 
queue = [[NSOperationQueue alloc] init]; 
[queue setMaxConcurrentOperationCount:4]; 


//In the thumbnail view the loadImage and unloadImage methods 
- (void) loadImage{ 
    if(!loaded){ 
     NSString *filename = [NSString stringWithFormat:@"%03d-cover-front", recipe.identifier, recipe.identifier]; 
     NSString *directory = [NSString stringWithFormat:@"RestaurantContent/%03d", recipe.identifier];  

     NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"png" inDirectory:directory]; 
     UIImage *image = [UIImage imageWithContentsOfFile:path]; 

     imageView = [[ImageView alloc] initWithImage:image andFrame:CGRectMake(0.0f, 0.0f, 176.0f, 262.0f)]; 
     [self addSubview:imageView]; 
     [self sendSubviewToBack:imageView]; 
     [imageView release]; 
     loaded = YES;  
    } 
} 

- (void) unloadImage{ 
    if(loaded){ 
     [imageView removeFromSuperview]; 
     imageView = nil; 
     loaded = NO; 
    } 
} 

Entonces mi carga y descarga de las operaciones:

- (id) initWithOperableImage:(id<OperableImage>) anOperableImage{ 

    self = [super init]; 
    if (self != nil) { 
     self.image = anOperableImage; 
    } 
    return self; 
} 

//This is the main method in the load image operation 
- (void)main { 
    [image loadImage]; 
} 


//This is the main method in the unload image operation 
- (void)main { 
    [image unloadImage]; 
} 

Respuesta

15

Estoy un poco confundido por el desplazamiento "espasmódico". Como NSOperationQueue ejecuta operaciones en hilos independientes, esperaba que en el peor de los casos aparecieran UIImageViews vacíos apareciendo en la pantalla.

En primer lugar, estaría buscando cosas que están impactando el procesador de manera significativa como NSOperation solo no debería interferir con el hilo principal. En segundo lugar, estaría buscando detalles sobre la configuración y ejecución de NSOperation que podrían estar causando problemas de bloqueo y sincronización que podrían interrumpir el hilo principal y, por lo tanto, afectar al desplazamiento.

Algunos elementos a tener en cuenta:

  1. Intente cargar sus ThumbnailView de con una sola imagen en la salida y la desactivación de la NSOperation cola (sólo pasa todo tras el "si se carga" check Esto le dará una. idea inmediata si el código NSOperation está afectando el rendimiento.

  2. Tenga en cuenta que pueden ocurrir -scrollViewDidScroll:muchos veces durante el curso de una sola acción de desplazamiento. Dependiendo de cómo los movimientos de desplazamiento y cómo su -centerThumbIndex es implementado, es posible que intente poner en cola las mismas acciones varias veces.Si ha contabilizado esto en su -initWithOperableImage o ha sido cargado, entonces es posible que su código aquí esté causando problemas de sincronización/bloqueo (vea 3 a continuación). Debe rastrear si se ha iniciado una operación NSO utilizando una propiedad "atómica" en la instancia de ThumbnailView. Evite poner en cola otra operación si esa propiedad está configurada y solo desarma esa propiedad (junto con la carga) al final de los procesos de NSOperation.

  3. Dado que NSOperationQueue opera en su (s) propio (s) hilo (s), asegúrese de que ninguno de los códigos que se ejecutan dentro de NSOperation se sincronice o bloquee con el hilo principal. Esto eliminaría todas las ventajas de usar NSOperationQueue.

  4. Asegúrese de que su operación de "descarga" tiene una prioridad menor que su operación de "carga", ya que la prioridad es la experiencia del usuario primero, la conservación de la memoria en segundo lugar.

  5. Asegúrese de mantener suficientes miniaturas para al menos una página o dos adelante y atrás, de modo que si NSOperationQueue se retrasa, tenga un alto margen de error antes de que las miniaturas en blanco se vuelvan visibles.

  6. Asegúrate de que la operación de carga solo esté cargando una miniatura "preescalada" y no cargue una imagen de tamaño completo ni cambie el tamaño ni el procesamiento. Esto sería una gran cantidad de gastos adicionales en el medio de una acción de desplazamiento. Vaya más allá y asegúrese de convertirlos a PNG16 sin un canal alfa. Esto proporcionará al menos una reducción (4: 1) en el tamaño con la esperanza de que no haya cambios detectables en la imagen visual. También considere usar imágenes de formato PVRTC que reducirán aún más el tamaño (reducción 8: 1). Esto reducirá en gran medida el tiempo que lleva leer las imágenes desde "disco".

Pido disculpas si algo de esto no tiene sentido. No veo ningún problema con el código que ha publicado y es más probable que surjan problemas en las implementaciones de su clase NSOperation o ThumbnailView. Sin revisar ese código, es posible que no describa las condiciones de manera efectiva.

Recomendaría publicar su código NSOperation para cargar y descargar y al menos una cantidad suficiente de ThumbnailView para comprender cómo interactúa con las instancias de NSOperation.

Espero que esto ayudó en cierta medida,

Barney

3

Una opción, aunque menos agradable a la vista, es cargar imágenes sólo cuando el desplazamiento se detiene.

establecer un indicador para desactivar la carga de imágenes en:

-scrollViewWillBeginDragging: 

la carga de imágenes volver a habilitar cuando el desplazamiento se detiene mediante el:

-scrollViewDidEndDragging:willDecelerate: 

UIScrollViewDelegate método. Cuando el parámetro willDecelerate: es NO, el movimiento se ha detenido.

2

el problema está aquí:

UIImage *image = [UIImage imageWithContentsOfFile:path]; 

Parece que roscado o no cuando se carga un archivo del disco (que tal vez eso sucede en el hilo principal independientemente, no estoy del todo seguro) todo se para. Normalmente no se ve esto en otras situaciones porque no se mueve una zona tan grande, si es que hay alguna.

1

Durante la investigación de este problema, encontré dos más recursos que pueden ser de interés:

compruebas el proyecto de ejemplo iPhone "PageControl": http://developer.apple.com/iphone/library/samplecode/PageControl/index.html

Se cargas perezosos ver los controladores en un UIScrollView.

  • y -

Mira la lib toque de cacao: http://github.com/facebook/three20 que tiene una clase 'TTPhotoViewController' que carga perezosos fotos/imágenes en miniatura de web/disco.

+0

Gracias por eso. Definitivamente voy a echar un vistazo. –

0

Establezca shouldRasterize = YES para la vista de contenido secundario a la vista de desplazamiento. Se ve para eliminar el comportamiento desigual de la vista de desplazamiento creada personalizada como un amuleto. :)

También haga algunos perfiles usando los instrumentos en el Xcode. Repase los tutoriales creados para perfilar por Ray Wenderlich me ayudó mucho.

Cuestiones relacionadas