在一个UIScrollView优化图像加载(Optimized Image Loading in a

2019-06-25 09:42发布

我有一个具有一组装载并排侧里面的图像的一个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];
            }
        }
    }
}

编辑:谢谢你的真正伟大的反应。 这是我的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];
}

Answer 1:

我有点疑惑的“干”的滚动。 由于NSOperationQueue运行在单独的线程(S)操作我会预计在最坏的情况可能会看到空的UIImageViews显示在屏幕上了。

首先,我会寻找的东西,单独显著影响的处理器的NSOperation不应与主线程干扰。 第二,我会寻找周边的NSOperation设置和执行,可能会造成锁定细节和同步可能中断主线程,因此影响滚动的问题。

有几个项目需要考虑:

  1. 尝试加载您ThumbnailView与在开始一个单一的形象和禁用的NSOperation排队(只跳过一切继“如果装”检查,这将立即给你想法的NSOperation代码是否影响性能。

  2. 请记住, -scrollViewDidScroll:单滚动操作的过程中,可能会发生很多次。 根据如何滚动移动以及您-centerThumbIndex实现你可能会尝试多次排队相同的动作。 如果你已经占了这个在你-initWithOperableImage或-loaded那么它有可能你在这里的代码导致同步/锁定的问题(见下文3)。 你应该跟踪是否已的NSOperation上使用ThumbnailView实例“原子”属性已经开始。 防止如果该属性在的NSOperation处理结束设定和取消设置仅该属性(以及加载的)队列另一操作。

  3. 由于NSOperationQueue在自己的线程(S)工作,确保没有你的代码的NSOperation内执行的是同步或锁定到主线程。 这将消除所有与使用NSOperationQueue的优势。

  4. 确保您的“卸载”的操作具有优先级低于你的“负荷”运转,因为优先级是用户体验第一,内存保护第二。

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

  6. 确保您的负荷运行时只加载“缩放前”缩略图,而不是加载完整大小的图像,并重新缩放或处理。 这将是一个很大的额外的开销在滚动操作的中间。 更进一步,并确保您将他们转换为PNG16没有alpha通道。 这将给出至少一个:在尺寸(4 1)减少与在可视图像希望没有可检测的变化。 也可以考虑使用将带大小下来,甚至进一步PVRTC格式的图像(8:1的减速比)。 这将大大减少需要从“硬盘”读取图像的时间。

我道歉,如果任何这是没有意义的。 我没有看到你发布和问题更有可能在你的NSOperation或ThumbnailView类的实现要发生的代码的任何问题。 在没有仔细检查代码,我可能不会有效地描述了条件。

我建议可以发布您的代码的NSOperation装卸和ThumbnailView至少不够了解它如何与实例的NSOperation交互。

希望这有助于在一定程度上,

巴尼



Answer 2:

一个选项中,虽然少视觉上令人满意,是只负载图像的滚动停止时。

设置一个标志,禁用图片加载中:

-scrollViewWillBeginDragging:

重新启用加载图像当滚动停止使用:

-scrollViewDidEndDragging:willDecelerate:

UIScrollViewDelegate方法。 当willDecelerate:参数是NO ,运动已经停止。



Answer 3:

问题就在这里:

 UIImage *image = [UIImage imageWithContentsOfFile:path];

看来,螺纹与否,当你从磁盘加载一个文件(这可能是在主线程上发生不分,我不能完全确定)的一切摊位。 你通常不认为这在其他情况下,因为你没有这么大的面积如果有的话动。



Answer 4:

虽然研究这个问题,我发现了两个更多的资源可能会感兴趣:

退房的iPhone样本项目“的PageControl”: http://developer.apple.com/iphone/library/samplecode/PageControl/index.html

它延迟加载在一个UIScrollView视图控制器。

  • 和 -

退房的可可触摸的lib: http://github.com/facebook/three20具有“TTPhotoViewController”类延迟加载照片/从网络/磁盘缩略图。



Answer 5:

设置shouldRasterize = YES用于子内容视图ADDE到滚动视图。 可以看出,除去像一个魅力的自定义创建的滚动视图的生涩行为。 :)

也可做使用Xcode的乐器一些剖析。 通过对分析创建的教程去雷Wenderlich它帮了我很多。



文章来源: Optimized Image Loading in a UIScrollView