2009-07-08 2 views
16

У меня есть UIScrollView, у которого есть набор изображений, загруженных бок о бок внутри него. Вы можете увидеть пример моего приложения здесь: http://www.42restaurants.com. Моя проблема связана с использованием памяти. Я хочу ленить загружать изображения, поскольку они собираются появиться на экране, и выгрузить изображения, которые не отображаются на экране. Как вы можете видеть в коде, я разрабатываю как минимум, какое изображение мне нужно загрузить, а затем назначить часть загрузки NSOperation и поместить его в NSOperationQueue. Все отлично работает, кроме отрывистого опыта прокрутки.Оптимизированная загрузка изображений в UIScrollView

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

- (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: Спасибо за очень большие ответы. Вот мой код NSOperation и ThumbnailView. Я пробовал пару вещей за выходные, но мне удалось улучшить производительность, приостановив очередь на работу во время прокрутки и возобновив ее при завершении прокрутки.

Вот мои фрагменты код:

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

Тогда моя загрузка и выгрузка операция:

- (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]; 
} 

ответ

15

Я немного озадачен «отрывистой» прокруткой. Поскольку NSOperationQueue запускает операции с отдельными потоками, я бы ожидал, что в худшем случае вы можете увидеть пустые UIImageViews, появляющиеся на экране.

Прежде всего я буду искать вещи, которые влияют на процессор значительно, поскольку NSOperation самостоятельно не должно мешать основной теме. Во-вторых, я буду искать детали, связанные с настройкой и выполнением NSOperation, которые могут вызвать проблемы блокировки и синхронизации, которые могут прерывать основной поток и, следовательно, влиять на прокрутку.

несколько пунктов, чтобы рассмотреть следующие вопросы:

  1. Попробуйте загрузить свои ThumbnailView с одного изображения на старте и отключение NSOperation очереди (просто пропустить все следующее: «если загружен» проверить Это даст вам. непосредственная идея ли код NSOperation это влияет на производительность.

  2. Имейте в виду, что -scrollViewDidScroll: может произойти много раз в течение одного действия прокрутки. в зависимости от того, как для движения прокрутки и как ваш -centerThumbIndex является вы можете пытаться поочередно выполнять одни и те же действия.Если вы учли это в своем -initWithOperableImage или -loaded, тогда его возможный код здесь вызывает проблемы с синхронизацией/блокировкой (см. 3 ниже). Вы должны отслеживать, было ли запущено NSOperation с использованием свойства «атом» в экземпляре ThumbnailView. Предотвратите очередность другой операции, если это свойство установлено, и только отмените это свойство (вместе с загруженным) в конце процессов NSOperation.

  3. Поскольку NSOperationQueue работает в своем потоке, убедитесь, что ни один из ваших кодов, выполняемых в NSOperation, не синхронизируется и не блокируется в основном потоке. Это исключило бы все преимущества использования NSOperationQueue.

  4. Убедитесь, что ваша операция «выгрузки» имеет более низкий приоритет, чем операция «нагрузка», так как приоритет - это опыт пользователя, а второй - сохранение памяти.

  5. Удостоверьтесь, что у вас недостаточно эскизов, по крайней мере, на одной или двух страницах вперед и назад, так что если NSOperationQueue отстает, у вас есть высокий запас ошибки перед тем, как станут видны невидимые миниатюры.

  6. Убедитесь, что ваша операция загрузки загружает только миниатюру с предварительным масштабированием, а не загрузку полноразмерного изображения и масштабирование или обработку. Это было бы большим количеством дополнительных накладных расходов в середине действия прокрутки. Идите еще дальше и убедитесь, что вы преобразовали их в PNG16 без альфа-канала. Это даст по крайней мере уменьшение (4: 1) в размерах, с надеждой не обнаруживаемое изменение визуального изображения. Также рассмотрите использование изображений формата PVRTC, которые еще больше уменьшат размер (уменьшение 8: 1). Это значительно сократит время, необходимое для чтения изображений с диска.

Приносим извинения, если это не имеет смысла. Я не вижу никаких проблем с кодом, который вы опубликовали, и проблемы, скорее всего, происходят в ваших реализациях NSOperation или ThumbnailView. Не рассматривая этот код, я не могу эффективно описывать эти условия.

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

Надеется, что это помогает в каком-то степени,

Barney

3

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

Установите флажок, чтобы отключить загрузку изображений в:

-scrollViewWillBeginDragging: 

повторного включения загрузки изображений, когда прокрутка останавливается с помощью:

-scrollViewDidEndDragging:willDecelerate: 

UIScrollViewDelegate метод. Когда параметр willDecelerate: равен NO, движение остановилось.

2

проблема здесь:

UIImage *image = [UIImage imageWithContentsOfFile:path]; 

кажется, что резьбовой или нет, когда вы загружаете файл с диска (который, возможно, что происходит на главной нитке, независимо от того, я не совсем уверен) все киоски. Обычно вы не видите этого в других ситуациях, потому что у вас нет такой большой площади, если она вообще есть.

1

Исследуя эту проблему, я нашел еще два ресурса, которые могут быть интересны:

ЗАКАНЧИВАТЬ проект образец iPhone «PageControl»: http://developer.apple.com/iphone/library/samplecode/PageControl/index.html

Это ленивые нагрузки просматривать контроллеры в UIScrollView.

  • и -

Заканчивать сенсорную какао LIB: http://github.com/facebook/three20, который имеет класс марки '' TTPhotoViewController что ленивые загружает фотографии/миниатюры веб/диска.

+0

Спасибо за это. Я обязательно посмотрю. –

0

Set shouldRasterize = YES для добавления дополнительного содержимого в прокрутку. Считается, что удаление отвратительного поведения настраиваемого вида прокрутки напоминает шарм. :)

Также выполните некоторые профилирования с использованием инструментов в Xcode. Пройдите через учебные пособия, созданные для профилирования Ray Wenderlich, это очень помогло мне.