从磁盘加载图像时滚动视图和表视图性能 [英] Scroll view and table view performance when loading images from disk

查看:125
本文介绍了从磁盘加载图像时滚动视图和表视图性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to improve the performance of my image-intensive iPhone app by using a disk-based image cache instead of going over the network. I've modeled my image cache after SDImageCache (http://github.com/rs/SDWebImage/blob/master/SDImageCache.m), and is pretty much the same but without asynchronous cache in/out operations.

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

I have some scroll views and table views that load these images asynchronously. If the image is on the disk, it's loaded from the image cache, otherwise a network request is made and the subsequent result is stored in the cache.

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

The problem I'm running into is that as I scroll through the scroll views or table views, there's a noticeable lag as the image is loaded from disk. In particular, the animation of going from one page to another on a scroll view has a small freeze in the middle of the transition.

我试图解决这个问题:


  • 使用NSOperationQueue和NSInvocationOperation对象发出磁盘访问请求(与SDImageCache相同),但它没有帮助

  • 调整滚动视图控制器代码,使其仅在滚动视图不再滚动时加载图像。这意味着磁盘访问仅在滚动视图停止滚动时触发,但如果我立即尝试滚动到下一页,我会注意到图像从磁盘加载时的延迟。

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

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

Note that I'm already caching the images in memory as well. So once everything is loaded into memory, the UI is nice and responsive. But when the app starts up, or if low memory warnings are dispatched, I'll experience many of these UI lags as images are loaded from disk.

相关代码片段在下面。我不认为我做任何花哨或疯狂的事情。 iPhone 3G上的延迟似乎并不明显,但在第二代iPod Touch上显而易见。

The relevant code snippets are below. I don't think I'm doing anything fancy or crazy. The lag doesn't seem to be noticeable on an iPhone 3G, but it's pretty apparent on an 2nd-gen iPod Touch.

图像缓存代码:

这是我的图片缓存代码的相关片段。非常简单。

Here's a relevant snippet of my image caching code. Pretty straightforward.

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

滚动视图控制器代码

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

Here's a relevant snippet of the code that I use for displaying images in my scroll view controllers.

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

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

The image loader object is just a lightweight object that checks the disk cache and decides whether or not to fetch an image from disk or network. Once it's done, it calls a method on the scroll view controller to display the image:

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

更新

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

I was experimenting with the app, and I disabled the image cache and reverted to always making network requests. It looks like simply using network requests to fetch images is also causing the same lag when scrolling through scroll views and table views! That is, when a network request finishes and the image is shown in the scroll view page or table cell, the UI slightly lags a bit and has a few split seconds of lag as I try to drag it.

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

The lag seems to be just more noticeable when using the disk cache, since the lag always occurs right at the page transition. Perhaps I'm doing something wrong when assigning the loaded image to the appropriate UIImageView?

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

Also - I've tried using small images (50x50 thumbnails) and the lag seems to improve. So it seems that the performance hit is due to either loading a large image from disk or loading a large image into an UIImage object. I guess one improvement would be to reduce the size of the images being loaded into the scroll view and table views, which was what I was planning to do nonetheless. However, I just don't understand how other photo-intensive apps are able to present what looks like pretty high-res photos in scrollable views without performance problems from going to disk or over the network.

推荐答案

如果你把它缩小到网络活动,我会尝试封装你的请求,以确保它是100%的主线程。虽然您可以异步使用NSURLConnection并响应它的委托方法,但我发现在后台操作中包装同步请求更容易。如果您的需求更复杂,您可以使用NSOperation或Grand central dispatch。 imageLoader实现中的一个(相对)简单示例可能是:

If you've narrowed it down to network activity I would try encapsulating your request to ensure it is 100% off of the main thread. While you can use NSURLConnection asynchronously and respond to it's delegate methods, I find it easier to wrap a synchronous request in a background operation. You can use NSOperation or grand central dispatch if your needs are more complex. An (relatively) simple example in an imageLoader implementation could be:

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

这篇关于从磁盘加载图像时滚动视图和表视图性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆