从磁盘加载图像时滚动视图和表视图性能

pmc*_*255 6 iphone caching image uitableview uiscrollview

我正在尝试通过使用基于磁盘的图像缓存而不是通过网络来提高我的图像密集型iPhone应用程序的性能.我在SDImageCache(http://github.com/rs/SDWebImage/blob/master/SDImageCache.m)之后模拟了我的图像缓存,并且几乎相同但没有异步缓存进/出操作.

我有一些滚动视图和表视图异步加载这些图像.如果映像在磁盘上,则从映像缓存加载,否则发出网络请求,后续结果存储在缓存中.

我遇到的问题是,当我滚动浏览滚动视图或表视图时,从磁盘加载图像时会出现明显的延迟.特别是,在滚动视图中从一个页面转到另一个页面的动画在转换过程中具有小的冻结.

我试图解决这个问题:

  • 使用NSOperationQueue和NSInvocationOperation对象来发出磁盘访问请求(与SDImageCache一样),但它根本没有帮助.
  • 调整滚动视图控制器代码,使其仅在滚动视图不再滚动时加载图像.这意味着磁盘访问仅在滚动视图停止滚动时触发,但如果我立即尝试滚动到下一页,我会注意到图像从磁盘加载时的延迟.

有没有办法让我的磁盘访问性能更好或对UI的影响更小?

请注意,我已经在内存中缓存了这些图像.因此,一旦将所有内容加载到内存中,UI就会很好并且响应迅速.但是当应用程序启动时,或者如果调度了低内存警告,我会遇到许多这些UI滞后,因为图像是从磁盘加载的.

相关的代码片段如下.我不认为我做任何花哨或疯狂的事情.在iPhone 3G上,这种延迟似乎并不明显,但在第二代iPod Touch上却非常明显.

图像缓存代码:

这是我的图像缓存代码的相关片段.非常直截了当.

- (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];
}
Run Code Online (Sandbox Code Playgroud)

滚动视图控制器代码

这是我用于在滚动视图控制器中显示图像的相关代码片段.

- (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"]];
    }
}
Run Code Online (Sandbox Code Playgroud)

图像加载器对象只是一个轻量级对象,它检查磁盘缓存并决定是否从磁盘或网络获取映像.完成后,它会调用滚动视图控制器上的方法来显示图像:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

我正在尝试应用程序,我禁用了图像缓存并恢复为始终发出网络请求.看起来简单地使用网络请求来获取图像在滚动滚动视图和表视图时也会导致相同的延迟!也就是说,当网络请求完成并且图像显示在滚动视图页面或表格单元格中时,当我尝试拖动它时,UI稍微滞后一点并且有几分钟的延迟.

使用磁盘缓存时滞后似乎更明显,因为滞后总是发生在页面转换时.将加载的图像分配给适当的UIImageView时,也许我做错了什么?

此外 - 我尝试使用小图像(50x50缩略图),滞后似乎有所改善.因此,性能影响似乎是由于从磁盘加载大图像或将大图像加载到UIImage对象中.我想一个改进是减少加载到滚动视图和表视图中的图像的大小,这是我计划做的事情.但是,我只是不明白其他照片密集型应用程序如何能够在可滚动视图中呈现看起来非常高分辨率的照片,而不会出现进入磁盘或通过网络出现性能问题.

spd*_*spd 0

在图像视图上绘制图像时,实际上会读取磁盘中的图像。即使我们缓存从磁盘读取的图像也不会产生影响,因为它只是保留对文件的引用。为此,您可能必须使用较大图像的平铺。

问候, 迪帕