title: Optimized Image Loading in a UIScrollView
body: <p>I have a UIScrollView that has a set of images loaded side-by-side inside it. You can see an example of my app here: <a href="http://www.42restaurants.com" rel="noreferrer">http://www.42restaurants.com</a>. My problem comes in with memory usage. I want to lazy load the images as they are about to appear on the screen and unload images that aren't on screen. As you can see in the code I work out at a minimum which image I need to load and then assign the loading portion to an NSOperation and place it on an NSOperationQueue. Everything works great apart from a jerky scrolling experience.</p>
  
  <p>I don't know if anyone has any ideas as to how I can make this even more optimized, so that the loading time of each image is minimized or so that the scrolling is less jerky.</p>
  
  <pre><code>- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
      [self manageThumbs];    
  }
  
  - (void) manageThumbs{
      int centerIndex = [self centerThumbIndex];
      if(lastCenterIndex == centerIndex){
          return;
      }
  
      if(centerIndex &gt;= totalThumbs){
          return;
      }
  
      NSRange unloadRange;
      NSRange loadRange;
  
      int totalChange = lastCenterIndex - centerIndex;
      if(totalChange &gt; 0){ //scrolling backwards
          loadRange.length = fabsf(totalChange);
          loadRange.location = centerIndex - 5;
          unloadRange.length = fabsf(totalChange);
          unloadRange.location = centerIndex + 6;
      }else if(totalChange &lt; 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 &lt; range.length &amp;&amp; range.location + i &lt; [scrollView.subviews count]; i++){
          UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)];
          if(subview != nil &amp;&amp; [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 &lt; range.length &amp;&amp; range.location + i &lt; [scrollView.subviews count]; i++){
          UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)];
          if(subview != nil &amp;&amp; [subview isKindOfClass:[ThumbnailView class]]){
              ThumbnailView *thumbView = (ThumbnailView *)subview;
              if(!thumbView.loaded){
                  LoadImageOperation *loadOperation = [[LoadImageOperation alloc] initWithOperableImage:thumbView];
                  [queue addOperation:loadOperation];
                  [loadOperation release];
              }
          }
      }
  }
  </code></pre>
  
  <p><strong>EDIT:</strong>
  Thanks for the really great responses. Here is my NSOperation code and ThumbnailView code. I tried a couple of things over the weekend but I only managed to improve performance by suspending the operation queue during scrolling and resuming it when scrolling is finished.</p>
  
  <p>Here are my code snippets:</p>
  
  <pre><code>//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;
      }
  }
  </code></pre>
  
  <p>Then my load and unload operations:</p>
  
  <pre><code>- (id) initWithOperableImage:(id&lt;OperableImage&gt;) 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];
  }
  </code></pre>