iOS性能优化--tableView懒加载图片


关于tableView的性能优化问题一直都是iOS开发者必备的一项优化技能, 可以好不夸张的说, app里面半数以上的页面都需要用到tableView; 对于它的优化网友也总结的非常详细, 鄙人就不详细解释了, 附个链接:
关于tableview的优化


本文主要介绍一下如何在tableView中实现懒加载图片, 别怪我多嘴, 一些初学者对懒加载的印象可能是这样的:

@interface ViewController()
@property (nonatomic,strong) NSMutableArray *lazyLoadArr;
@end
@implementation ViewController
- (NSMutableArray *)lazyLoadArr
{
    if (!_lazyLoadArr) {
        _lazyLoadArr = [NSMutableArray array];
    }
    return _lazyLoadArr;
}
@end

实例中定义了一个懒加载的可变数组, 重写数组的getter方法, 在该方法中判断是否存在lazyLoadArr, 如果存在就返回, 不存在就初始化, 这样保证数组只加载了一次; 而这个只是懒加载中比较常见的一种用法;
那么什么是懒加载呢?

懒加载也叫延迟加载, 或者通俗来讲就是用到时再去加载对象

也就是说用到这种思想的基本上都属于懒加载;


其实在tableView懒加载图片也是app中比较常见的功能; 它可以可以减轻服务器的压力, 节约流量, 提高页面的加载速度等等, 费这么大劲儿, 最终也是为了更好的用户体验;
先附上实现效果图, 让大家感受一下:

效果图.gif

这个例子节选自苹果的官方例子LazyTableImages实现图片懒加载;
LazyTableImages下载

OK, 现在我们开始剖析一下代码;
附上工程目录:

工程目录.png

首先说一下细节部分, 其实也不是什么大问题, 留意一下:
①工程里面的AppDelegate类被替换成LazyTableAppDelegate, 其实在函数入口main.m替换下类就行;
②pch文件导入了Foundation, UIKit框架全局调用, 所以出现了以下情况:

AppRecord.h

一般对对象的基础操作都放在了Foundation.h, 为了方便, 通常也就直接引入<Foundation/Foundation.h>;

数据请求

为了减少数据延迟加载的卡顿感, demo里直接把数据请求写在了LazyTableAppDelegate文件中; 选一小段代码说一下:

            __weak ParseOperation *weakParser = self.parser;
            self.parser.completionBlock = ^(void) {
                [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                if (weakParser.appRecordList != nil)
                {
                    dispatch_async(dispatch_get_main_queue(), ^{    
                        RootViewController *rootViewController =
                            (RootViewController*)[(UINavigationController*)weakSelf.window.rootViewController topViewController];
                        rootViewController.entries = weakParser.appRecordList;
                        [rootViewController.tableView reloadData];
                    });
                }
                weakSelf.queue = nil;
            };     
            [self.queue addOperation:self.parser];
  • block循环引用问题, 这个经常遇到, 就不多说了;
  • 数据请求是在子线程里进行了, 然而要想对UI进行操作, 需要回到主线程, 刷新数据;
  • 对于networkActivityIndicatorVisible, 这个是UIApplication的一个属性, 用于在状态栏上显示菊花转的图标,表明网络状态, 而我们平常都是用MB或者SVP来表示网络加载的, 有可能没见过, 说明一下;

UI部分(tableView)

看了示意图我们发现只有在tableView松开的时候才加载图片, 那这个是怎么实现的呢, 我从tableView的cellForRowAtIndexPath:代理方法中选了一段代码:

         if (!appRecord.appIcon)
            {
                if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
                {
                    [self startIconDownload:appRecord forIndexPath:indexPath];
                }
                cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"];                
            }
            else
            {
               cell.imageView.image = appRecord.appIcon;
            }

当没有下载好的图片, 即!appRecord.appIcon时, 判断是否停止拖拽(self.tableView.dragging == NO)或者加速度为0(self.tableView.decelerating == NO), 都满足的话就下载图片, 这样就实现了图示效果;
但是光这样处理是不够的, 如果连续拖动, 不让tableView停止, 里面的图片是空的, 所以还要进行下面的操作:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (!decelerate)
    {
        [self loadImagesForOnscreenRows];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self loadImagesForOnscreenRows];
}

由于tableView继承了scrollView, 所以才在draggingdecelerating结束的时候加载图片, 最终实现了图示效果;

优化亮点

一. 图片圆角优化

一般在开发中我们是这样给图片切圆角的:

      myImage.layer.cornerRadius = 50;
      myImage.layer.masksToBounds = YES;

其中masksToBounds(CALayer)表示视图的图层上的子图层,如果超出父图层的部分就截取掉;还有clipsToBounds(UIView),是指视图上的子视图,如果超出父视图的部分就截取掉。
在iOS9.0之前这样设置会触发离屏渲染,比较消耗性能。尤其是tableView中(处理了大量图片),会明显的感到页面有轻微卡顿。
那要怎么处理呢, 一般我们用Core Graphics绘制圆角, 附上工程里的代码:

      CGSize itemSize = CGSizeMake(kAppIconSize, kAppIconSize);
      UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0f);
      CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
      [image drawInRect:imageRect];
      self.appRecord.appIcon = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
二. model的小技巧

我们发现在工程中, 无论对于tableView来说, 还是下载图片来说, 都是对同一个对象进行操作, demo中把tableView数据里面的对象扩展了下面这两条属性:

@property (nonatomic, strong) UIImage *appIcon;
@property (nonatomic, strong) NSString *imageURLString;

这样我们去下载图片的时候, 只需要传进去一个AppRecord类的对象, 返回来也是同一个对象:

     // 将下载完成的网络图片的data值传给这个对象
    UIImage *image = [[UIImage alloc] initWithData:data];
    self.appRecord.appIcon = image;

有人要问了只是简化了传入的数据嘛, 没什么特别的, 但是你细看tableView里面cellForRowAtIndexPath方法就会发现, 这样做既简化了逻辑判断, 又易于理解, 因为是同一个对象;

    AppRecord *appRecord = (self.entries)[indexPath.row];
      if (!appRecord.appIcon)         //  简便的判断条件
            {
                if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
                {
                    [self startIconDownload:appRecord forIndexPath:indexPath];
                }
                cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"];                
            }
            else
            {
               cell.imageView.image = appRecord.appIcon;
            }

这个思想真是不由得让老铁双击666啊, 非常棒👍;

好的, 这次就分享那么多, 以后还得研究更多更深的源码, 加油⛽️;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容