前言:相信大家都使用过SDWebImage这么一个三方框架吧.对,这个框架写的真心的不错.使用过这个框架,就一定要了解它内部的实现过程,否则糊里糊涂的使用,那天不更新了,怎么办.
这篇文章主要是介绍SDWebImage加载网络图片的基本原理.
tableView是我们做APP的时候,使用率最多的一个控件了,我敢说一个app没有tableView的话,它都不好意思说自己是个app,更别说上架了.
说到tableView,其实它本身就是一个比较好的控件,既继承了scrollView的滚动,又有缓存池这么一个特别的存储方式(重复利用) .但是一旦涉及到网络,它变有点捉襟见肘了,异步请求,主线程更新,cell高度 等等的问题,造成了它的显示问题.
下来我给大家说一说,初步的优化方案.
1.我一用户为出发点进行说明,用户肯定要使用比较省流量的客户端,如果进一次就下载一次,是不是很耗流量.
对应的,我们不应该直接从网络上直接请求数据, 应该先从用户手机的cache中取搜寻(哪个文件夹---->根据你从网络上请求的数据下载到哪. 为什么是cache,这个是苹果沙盒中就只有这个文件夹,我们还能存了,其他的不好意思,苹果会做特殊处理的 :document--->这里放东西,对不起,别想上架了.preference---> 偏好设置, 一般放配置相关的参数. temp--->临时数据(会自动删除的) )
- 网络请求的存放方式 ---->1>直接存放在cache.2>更新显示.
这里对于网络请求我们也需要优化,保证每个下载只有一个进程,当图片下载完成后,要及时移除进程.如果当前cell下载已有进行,就不必去创建新的线程进行下载. 如果图片长时间无法被下载下来,要及时停止当前的任务(双进程,可能下载好了以后又会被新进程的下载覆盖)
3.对于用户,最快的方式读取数据,不应该是什么路径读取,而是自己的程序内直接读取最好.(所以网络上下载的数据应该在程序内也保存一份.)
不用担心,程序会变大,应为这一部分数据,只要已退出界面,就会马上被移除的
下载用一张图表进行一个详细的说明
如果没有沙盒的话:
如果有沙盒的话:
下面我把代码程序附带一份---->仅供参数
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"app"];
AppModel *appModel = self.apps[indexPath.row];
cell.textLabel.text = appModel.name;
cell.detailTextLabel.text = appModel.download;
// 占位图片
cell.imageView.image = [UIImage imageNamed:@"kk_fruit_link_180px_1186831_easyicon.net"];
// 从图片缓存中取, 如果取不到
if (!self.iconCache[appModel.icon]) {
NSLog(@"没有缓存");
NSString *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@",cache);
NSString *file = [appModel.icon lastPathComponent];
NSString *path = [cache stringByAppendingPathComponent:file];
__block NSData *data = [NSData dataWithContentsOfFile:path];
// 从内存中取,如果取不到
if (data == nil) {
NSLog(@"没有数据");
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *blockOpertion = self.blockOperation[appModel.icon];
// 判断是否被下载,若果没有下载线程
if (blockOpertion == nil) {
NSLog(@"没有数据,没有线程");
blockOpertion = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:appModel.icon];
data = [NSData dataWithContentsOfURL:url];
// 有线程,但没有下载下来的话,删除线程,重新下载 (保证一个图片,只有一个线程来控制)
if (data == nil) {
NSLog(@"下载不下来,需要重新下载");
[self.blockOperation removeObjectForKey:appModel.icon];
return ;
}
// 下载下来的数据,返回图片
UIImage *image = [UIImage imageWithData:data];
// 图片放入缓存
self.iconCache[appModel.icon] = image;
// 数据进行储存
[data writeToFile:path atomically:YES];
// 将图片返回主线程,并赋值UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
// cell.imageView.image = self.iconCache[appModel.icon];
// 刷新数据 ,不让数据复用
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
// 下载成功移除操作
[self.blockOperation removeObjectForKey:appModel.icon];
}];
}];
// 添加操作到队列中
[queue addOperation:blockOpertion];
}
}else{
// 如果内存中有数据
NSLog(@"没有缓存,但有数据");
// 取出数据,返回图片
UIImage *image = [UIImage imageWithData:data];
// 图片放入缓存
self.iconCache[appModel.icon] = image;
// UI赋值
cell.imageView.image = self.iconCache[appModel.icon];
}
}
else{
// 如果有缓存。直接赋值
NSLog(@"有缓存");
cell.imageView.image = self.iconCache[appModel.icon];
}
return cell;
}
这里我要说明的是,我是以模型为基本跨度,用模型属性参数,作为键, 用来保存任务,以及程序的缓存. 并且也组成文件存储的全路径. 这样的话,比较方便存取数据. 因为本来就是要取出模型数据,因为模型数据的不同,而赋了不同的值. 又方便,又具有区分性.
这里建立了两个字典 : 1.iconCache:保存缓存的图片
2.blockOperation 用来保存 下载任务 (程序里面有一个没有定义好 ,结果有个很像的名字NSBlockOperation *blockOpertion 这个是定义任务的. 对不起各位了).
说到这里,总算将网络处理这方面讲的差不多了, 现在就来说说SDWebImage的强大.
SDWebImage的操作
对于以上的程序在SDWebImage中只需要一句代码就已经处理完全了.
在它的"UIImageView+WebCache.h"中已经将这部分代码已经封装完全了.
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:appModel.icon] placeholderImage:image];
tableView的二次优化
这里就需要用到绘图的知识了.什么是imageView,其实它根本就是在一个view的上面图层上进行绘画,将图片画上去,然后将用户的交互给取了而已. 那我们是不是可以将cell 的imageView也给取了,然后只建立一个空白的view,在上面绘画就行了. 这样就会相比与以前少了一些imageview. 相当于 节省了很多控件吧. (性能相对也会减少不少).
我曾经想过一个问题, 比如绘画 (会不会很耗时), 其实imageView何尝不是一个耗时的操作(它又要创建,又要画), 从这里就可以看出,绘画是一个不错的选择.
可能大家觉得绘画,多么麻烦呀. 不错确实很麻烦,特别是cell的高度不确定得时候,会显得额外麻烦.但是我想你的服务器也不会傻到不给你传想过图片的宽高吧, 对于多组图片你利用9宫格思想将其缩小绘画(又美观又节省控件和空间).
如果imageview真的很多的时候,绘画的优势就体现出来了,因为控件加载的少了,相当于系统的消耗就会变得很少,系统就会流畅了很多.
这段代码我就不着急写了. 谢谢了. (我想说程序是有感情的,有思想的, 他们的这些,其实都是我们程序员所赋予的, 所以少年不断的学习吧, 让自己喜欢上思考,喜欢上编程吧, 让自己的程序有感情吧).