前言
在iOS开发中从网络加载图片是一个比较值得思考的问题,因为你要考虑用户的体验,这其实包括流畅度,以及用户的流量考虑,那么今天我就来简单的说点这方面知识。
具体实现:
说到缓存就可以分为内存缓存和沙盒缓存,内存缓存的话就是用简单的用一个字典来记录下载的图片。
今天的环境就是从网络下载一些图片给tableview的imageView的image赋值,SAMApp是模型类,icon是url.
1.定义几个属性,具体如下
/** 所有数据 */@property (nonatomic, strong) NSArray *apps;/** 内存缓存的图片 */@property (nonatomic, strong) NSMutableDictionary *images;/** 记录正在下载的任务 */@property (nonatomic, strong) NSMutableDictionary *operations;/** 队列对象 */@property (nonatomic, strong) NSOperationQueue *queue;
2.判断内存中有没有值,这里面把url当做key来从字典中取值,相当于内存中取值,如何有值那就直接给imageView的image赋值
// 从内存中取出图片 UIImage *image = self.images[app.icon]; if (image) { // 内存里面有值 cell.imageView.image = image;
3.如何内存中没有值,那么就去沙盒中检查有否有需要的图片,这里面把url当作最后的路径目录
// 获取cache目录 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES) firstObject]; // 获取文件名 NSString *filePath = [app.icon lastPathComponent]; // 计算出全部路径 NSString *file = [cachePath stringByAppendingString:filePath]; // 加载沙盒中的文件数据 NSData *data = [NSData dataWithContentsOfFile:file];
4.如果以上情况都没有,这时应该添加占位视图,如果你是自定义的cell可能不需要,但是系统的UITableViewCell是需要的,要不然需要拖动之后才会显示图片。
5.这时我们就应该开启线程从网上下载图片,如果只是简单的开启线程下载,这个过程会出现图片的位置不对,搬动刷新之后才正常的状况,这是由于cell的重利用导致的,当图片正在下载的时候,你这个cell可能重利用到下一个位置上去了,然后正好下载结束就会产生错误的图片加载。如何解决呢?
解决的方法其实很简单,因为我们下载的每一个图片我们都需要去开起一个NSOperation,这时我们用url为key的字典来记录这些正在下载的operation.每次先去这些operation里面取,如果有说明是正在下载,那么就不开起线程,如果没有在开起。
没有的情况下,说明我们需要开起线程,在这种情况下我们需要注意一种情况,就是如何下载失败的情况下我们就要反这个key从字典里面移除,要不然这一个图片就会不能再下载。
核心代码:
if (image) { // 内存里面有值 cell.imageView.image = image; } else { //从沙盒里面取 // 获取cache目录 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES) firstObject]; // 获取文件名 NSString *filePath = [app.icon lastPathComponent]; // 计算出全部路径 NSString *file = [cachePath stringByAppendingString:filePath]; // 加载沙盒中的文件数据 NSData *data = [NSData dataWithContentsOfFile:file]; // 沙盒里面是否有数据 if (data) { // 有数据直接给图片赋值 UIImage *image = [UIImage imageWithData:data]; cell.imageView.image = image; // 重新存到内存里面 self.images[app.icon] = image; } else { // 占位视图 cell.imageView.image = [UIImage imageNamed:@"1"]; NSOperation *operation = self.operations[app.icon]; // 该任务是否正在下载 if (operation == nil) { // 没有下载,创建 operation = [NSBlockOperation blockOperationWithBlock:^{ NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; if (data == nil) { [self.operations removeObjectForKey:app.icon]; return ; } UIImage *image = [UIImage imageWithData:data]; // 记录到内存字典 self.images[app.icon] = image; // 回到主线程显示图片 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 刷新指定行的数据 [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; // 把二进制文件写入沙盒 [data writeToFile:file atomically:YES]; // 下载任务完成,移除记录的操作 [self.operations removeObjectForKey:app.icon]; }]; //加入队列 [self.queue addOperation:operation]; // 记录正在下载的操作 self.operations[app.icon] = operation; } } }