UIScrollView中的优化图像加载

Mic*_*ord 16 iphone objective-c uiscrollview uiimage

我有一个UIScrollView,其中包含一组并排加载的图像.你可以在这里看到我的应用程序的一个例子:http://www.42restaurants.com.我的问题在于内存使用情况.我想懒得加载图像,因为它们即将出现在屏幕上并卸载不在屏幕上的图像.正如您在代码中看到的那样,我至少需要加载哪个图像,然后将加载部分分配给NSOperation并将其放在NSOperationQueue上.除了生涩的滚动体验之外,一切都很棒.

我不知道是否有人有任何关于如何使其更加优化的想法,以便每个图像的加载时间最小化或使滚动不那么生涩.

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

编辑: 谢谢你真的很好的回应.这是我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我的加载和卸载操作:

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

Bar*_*tox 15

我对"生涩"的滚动感到有些困惑.由于NSOperationQueue在不同的线程上运行操作,我预计在最坏的情况下你可能会看到空的UIImageViews出现在屏幕上.

首先,我会寻找影响处理器的事情,因为NSOperation本身不应该干扰主线程.其次,我将查找有关NSOperation设置和执行的详细信息,这可能导致锁定和同步问题,这些问题可能会中断主线程,从而影响滚动.

一些要考虑的事项:

  1. 尝试在开始时使用单个图像加载ThumbnailView并禁用NSOperation排队(只需跳过"if loaded"检查后的所有内容.这将使您立即了解NSOperation代码是否会影响性能.

  2. 请记住,-scrollViewDidScroll:可能会发生许多单滚动操作过程中的时间.根据滚动的移动方式以及-centerThumbIndex实现方式,您可能会尝试多次对相同的操作进行排队.如果您在-initWithOperableImage中已经考虑到这个问题或者已经加载了 - 那么您的代码可能导致同步/锁定问题(参见下面的3).您应该跟踪是否已使用ThumbnailView实例上的"atomic"属性启动了NSOperation.如果设置了该属性,则阻止对另一个操作进行排队,并且仅在NSOperation进程结束时取消设置该属性(以及加载).

  3. 由于NSOperationQueue在其自己的线程中运行,因此请确保在NSOperation中执行的任何代码都不会同步或锁定到主线程.这将消除使用NSOperationQueue的所有优点.

  4. 确保"卸载"操作的优先级低于"加载"操作,因为优先级是用户体验优先,内存节省第二.

  5. 请务必保持足够的缩略图至少一个或两个页面的前进和后退,这样,如果NSOperationQueue落后,你有错误的高利润面前空白缩略图变得可见.

  6. 确保您的加载操作仅加载"预缩放"缩略图,而不加载完整大小的图像并重新缩放或处理.在滚动动作的中间,这将是很多额外的开销.更进一步,确保你没有alpha通道将它们转换为PNG16.这将使尺寸减小至少(4:1),并且希望视觉图像中没有可检测到的变化.还要考虑使用PVRTC格式图像,这样可以进一步缩小尺寸(减少8:1).这将大大减少从"磁盘"读取图像所需的时间.

如果任何一个没有意义,我道歉.我没有看到您发布的代码存在任何问题,并且更有可能在您的NSOperation或ThumbnailView类实现中出现问题.如果不审查该代码,我可能无法有效地描述这些条件.

我建议发布你的NSOperation代码来加载和卸载,至少足够的ThumbnailView来了解它如何与NSOperation实例交互.

希望这在某种程度上有所帮助,

巴尼