多线程24——多图下载4_第四种实现方式(缓存使用NSCache)
一、修改与说明
这种方式的修改是在 iOS学习笔记-123.多线程22——多图下载2_第二种实现方式(三级缓存) 的基础上修改的。
前面我们使用了 字典 做内存缓存,现在我们改为 NSCache 来做内存缓存。
之前在 iOS学习笔记-130.NSCache使用 中我们说过 NSCache的使用和字典几乎是一样的,只有部分区别。
于是,我们这里只需要修改 images 这个缓存属性的 类型,和实例化时候的方法。
修改如下
1.
/** 内存缓存 */
@property (nonatomic, strong) NSMutableDictionary *images;
修改为
/** 内存缓存 */
@property (nonatomic, strong) NSCache *images;
2.
-(NSMutableDictionary*)images{
if(_images==nil){
_images = [NSMutableDictionary dictionary];
}
return _images;
}
修改为,这里面我们加了缓存数量的限制
-(NSCache*)images{
if(_images==nil){
_images = [NSCache new];
//最大缓存10张图片
_images.countLimit = 10;
}
return _images;
}
二、代码
//
// ViewController.m
// 03_UIview86多线程_多图下载
//
// Created by 杞文明 on 17/9/6.
// Copyright © 2017年 杞文明. All rights reserved.
//
#import "ViewController.h"
#import "QWMAppItem.h"
#import "NSString+MD5.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()
/** tableView的数据源 */
@property (nonatomic, strong) NSArray *apps;
/** 内存缓存 */
@property (nonatomic, strong) NSCache *images;
/** 队列 */
@property (nonatomic, strong) NSOperationQueue *queue;
/** 操作缓存 */
@property (nonatomic, strong) NSMutableDictionary *operations;
@end
@implementation ViewController
-(NSArray*)apps{
if(_apps==nil){
//字典数据
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
//字典数据转模型数据
NSMutableArray *arrM = [NSMutableArray array];
for (NSDictionary *dict in array) {
[arrM addObject:[QWMAppItem appWitdDict:dict]];
}
_apps = arrM;
}
return _apps;
}
-(NSCache*)images{
if(_images==nil){
_images = [NSCache new];
//最大缓存10张图片
_images.countLimit = 10;
}
return _images;
}
-(NSOperationQueue *)queue{
if(_queue == nil){
_queue = [[NSOperationQueue alloc]init];
//设置最大并发数
_queue.maxConcurrentOperationCount = 5;
}
return _queue;
}
-(NSMutableDictionary *)operations{
if(_operations == nil){
_operations = [NSMutableDictionary dictionary];
}
return _operations;
}
/*组数*/
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
/*每组的行数*/
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.apps.count;
}
/*每个cell*/
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString* identifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
//获取数据
QWMAppItem *appItem = self.apps[indexPath.row];
cell.textLabel.text = appItem.name;
cell.detailTextLabel.text = appItem.download;
[self handle2:cell withItem:appItem withIndex:indexPath];
return cell;
}
/*图片的第二种种处理方式*/
-(UITableViewCell*)handle2:(UITableViewCell*)cell withItem:(QWMAppItem*)appItem withIndex:(NSIndexPath*)indexPath{
//1.首先判断内存中是否已经存在,存在直接取
//2.内存中不存在,判断本地是否存在,存在直接取。
//3.本地不存在,判断当前的图片是否已经添加到任务中,如果是,那么不操作
//4.如果没有添加到任务中,那么我们把下载图片的操作添加到任务中
//获取我们图片地址的MD5值作为唯一标识符 MD5+后缀名
NSString *imageID = [NSString stringWithFormat:@"%@.%@",[appItem.icon MD5_32BitLower],[appItem.icon pathExtension]];
UIImage *image = [self.images objectForKey:imageID];
if(image){
//使用内存缓存
cell.imageView.image = image;
NSLog(@"%zd处的图片使用了内存缓存中的图片",indexPath.row);
}else{
//1.获取沙盒的路径
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// NSLog(@"%@",caches);
//2.拼接我们图片的地址
NSString * imageFullPath = [caches stringByAppendingPathComponent:imageID];
//3.获取本地磁盘中的图片数据
NSData *imageData = [NSData dataWithContentsOfFile:imageFullPath];
//4.判断数据是否已经存在,存在说明我们有本地缓存,那么我们直接拿来显示
if (imageData) {
//使用本地缓存
cell.imageView.image = [UIImage imageWithData:imageData];
NSLog(@"%zd处的图片使用了本地缓存中的图片",indexPath.row);
}else{
//都没有,那么我们就需要去网络下载了
//我们获取先去判断一下,我们的这个图片下载任务是否已经在队列中存在,如果有不操作,没有那么我们添加一个下载任务
NSBlockOperation *download = [self.operations objectForKey:imageID];
if(!download){//没有这个任务,那么我们就添加任务
//为了解决滑动时候数据显示错乱,我们现在添加一张图片,用于做默认显示
cell.imageView.image = [UIImage imageNamed:@"image1"];
//创建现在任务
download = [NSBlockOperation blockOperationWithBlock:^{
//下载
NSURL *url = [NSURL URLWithString:appItem.icon];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
//容错处理
if (image == nil) {
//如果这个下载有误,那么我们不操作了,并且从操作缓存中移除,为了下一次还能下载
[self.operations removeObjectForKey:imageID];
return ;
}
//下载好了,现在需要如下操作
//1.数据添加到内存缓存中
//2.数据添加到本地中(本地缓存)
//3.主线程中显示数据
[self.images setObject:image forKey:imageID];//内存缓存
[imageData writeToFile:imageFullPath atomically:YES];//本地缓存
[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{//主线程显示,就是去刷新这一行
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}]];
//下载已经好了,那么移除图片下载操作
[self.operations removeObjectForKey:imageID];
}];
//把任务添加到队列中
[self.queue addOperation:download];
//添加任务到操作缓存中,等到下载好了再把它移除
[self.operations setObject:download forKey:imageID];
}
}
}
return cell;
}
/*内存警告*/
-(void)didReceiveMemoryWarning{
//内存不存的时候删除我们的缓存
[self.operations removeAllObjects];
[self.images removeAllObjects];
}
@end