博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS开发>学无止境 - Cell异步图片加载优化,缓存机制详解
阅读量:4588 次
发布时间:2019-06-09

本文共 3029 字,大约阅读时间需要 10 分钟。

作者:勤奋的笨老头

网址:http://www.jianshu.com/p/02ab2b74c451

 

最近研究了一下UITbleView中异步加载网络图片的问题,iOS应用经常会看到这种界面。一个tableView上显示一些标题、详情等内容,在加上一张图片。这里说一下这种思路。

 

为了防止图片多次下载,我们需要对图片做缓存,缓存分为内存缓存于沙盒缓存,我们当然两种都要实现。

 

由于tableViewCell是有重用机制的,也就是说,内存中只有当前可见的cell数目的实例,滑动的时候,新显示cell会重用被滑出的cell对象。这样就存在一个问题:

 

一般情况下在我们会在cellForRow方法里面设置cell的图片数据源,也就是说如果一个cell的imageview对象开启了一个下载任务,这个时候该cell对象发生了重用,新的image数据源会开启另外的一个下载任务,由于他们关联的imageview对象实际上是同一个cell实例的imageview对象,就会发生2个下载任务回调给同一个imageview对象。这个时候就有必要做一些处理,避免回调发生时,错误的image数据源刷新了UI。

 

所以在我们向下滑动tableview的时候我们需要手动去取消掉下载操作,当用户停止滑动,再去执行下载操作。SDWebImage采用的也是这种策略。

 

很简单我们只需要监听ScrollView的代理方法(tableview继承自Scrollview)。

 

/**

* 当用户开始拖拽表格时调用

*/

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

{

// 暂停下载

[self.queue setSuspended:YES];

}

/**

* 当用户停止拖拽表格时调用

*/

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

{

// 恢复下载

[self.queue setSuspended:NO];

}

 

而SDWebImage才用的就是这种方式,所以在运行SDWebImage的demo的时候,可以看到,如果快速滑下去,然后又滑回来的话,图片是过了一会才显示出来,这是因为快速滑动的时候,旧数据源的下载任务被取消掉了。

 

/-------------------------------------------

 

下面介绍一下具体的思路。

 

异步下载图片我们用的是NSOperation,并且创建一个全局的queue来管理下载图片的操作。

 

/**

* 存放所有下载操作的队列

*/

@property (nonatomic,strong) NSOperationQueue* queue;

 

另外需要两个字典operations、images

 

/**

* 存放所有的下载操作(url是key,operation对象是value)

*/

@property (nonatomic,strong) NSMutableDictionary* operations;

/**

* 存放所有下载完成的图片,用于内存缓存,同样用Url为key

*/

@property (nonatomic,strong) NSMutableDictionary* images;

 

在把图片显示到Cell上之前:

 

先判断内存中(images字典中)有没有图片,

如果有,则取出url对应的图片来显示,
如果没有,再去沙盒缓存中查看,当然存到沙盒中都是NSData。

如果沙盒缓存中有,我们取出对应的数据给Cell去显示

如果沙盒中也没有图片,我们先显示占位图片。再创建operation去执行下载操作了。
当然在创建operation之前,我们要判断这个operation操作是否存在

 

这个时候就用到我们operations这个字典了

 

//取出当前URL对应的下载操作

NSBlockOperation* operation = self.operations[app.icon];

 

如果没有下载操作,我们才需要真正的去创建operation执行下载。

 

创建好下载操作之后应该把该操作存放到全局队列中去异步执行,同时吧操作放入operations字典中记录下来。

 

//添加操作到队列中

[self.queue addOperation:operation];

//添加到字典中

self.operations[app.icon] = operation;

 

下载完成之后:

把下载好的图片放到内存中、同时存到沙盒缓存中

 

下面存放到沙盒中的代码可以定义成宏,具体可以下载后面的demo

 

if (image) { //防止下载失败为空赋值造成崩溃

vc.images[app.icon] = image;

//下载完成的图片存入沙盒中

// UIImage --> NSData --> File(文件)

NSData* ImageData = UIImagePNGRepresentation(image);

NSString* CachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

NSString* filePath = [CachesPath stringByAppendingPathComponent:[app.icon lastPathComponent]];

[ImageData writeToFile:filePath atomically:YES];

}

 

执行完上面的操作之后回到主线程刷新表格,

 

从operations字典中移除下载操作(防止operations越来越大,同时保证下载失败后,能重新下载)

 

//刷新当前行的图片数据

self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

 

这里我们不用[self.tableView reloadata],因为会刷新整个cell,浪费性能。

 

当然如果你的下载操作里面需要做的事情很多的时候,可以考虑自定义operation。 由于我这里只是简单的下载小图就没有自定义operation了。注意要自己创建自动释放池。

另外清理缓存的功能也没有做
有兴趣的朋友可以自己尝试下

 

渣渣代码下载:https://github.com/hongfenglt/CellDownloadImage

 

至此基本实现思路已经完成了,这时候你会发现其实真正在项目中,以上代码并没有什么卵用。因为这个操作,SDWebImgae已经都封装好,用一句代码就搞定了,并且这句代码代表的内容比上面的操作更加完善。⊙﹏⊙‖∣°

 

以上内容只是简单的优化,想更深层去优化操作可以参考下面这篇文章:http://ios.jobbole.com/82374

转载于:https://www.cnblogs.com/xvewuzhijing/p/5003748.html

你可能感兴趣的文章
块状元素和内联元素
查看>>
nav元素
查看>>
内存对齐
查看>>
HTML及资源是如何load的
查看>>
虚拟机apache启动
查看>>
【Linux】Centos下安装ffmpeg
查看>>
VSCode使用随笔
查看>>
MySQL 常用命令
查看>>
nginx FastCGI配置 No input file specified
查看>>
iOS - 拓展
查看>>
Windows命令远程执行工具Winexe
查看>>
XamarinAndroid组件教程RecylerView动画组件使用动画(3)
查看>>
linux vim 配置 go 开发环境
查看>>
week 6 CORS
查看>>
Openstack Neutron:二层技术和实现
查看>>
组合设计模式
查看>>
第十五部分_Struts2.1拦截器深度剖析、异常处理
查看>>
[codevs1286]郁闷的出纳员
查看>>
Python匿名函数详解
查看>>
python面向对象(六)之元类
查看>>