iOS干货-->图片二级缓存机制(简仿SDWebImage内部实现)

iOS干货-->图片二级缓存机制(简仿SDWebImage内部实现)_第1张图片
xcode.png

图片二级缓存机制(简仿SDWebImage内部实现)

本项目实现了多图下载,内部是二级缓存机制,既在显示图片之前会先去内存缓存中找,如果找到了就显示,如果没有就去磁盘中加载二进制数据,如果磁盘中也没有就会去下载。
此外,该项目做了一定的容错处理,具体代码如下,详细内容都加了注释,如果有什么建议,欢迎前来讨论,如果需要源代码可在评论里留下联系方式,看到后会在第一时间发送,好了,不多说了,代码如下:

#import "ViewController.h"
#import "WJCellItem.h"
@interface ViewController ()
//定义数组来存储从plist中加载的数据
@property (strong,nonatomic)NSArray *dataArr;
//在内存中存储图片的容器
@property (strong,nonatomic)NSCache *images;
//存储操作的字典
@property (strong,nonatomic)NSMutableDictionary *operations;
//队列
@property (strong,nonatomic)NSOperationQueue *queue;
@end

@implementation ViewController
//懒加载创建存储数据的数组
-(NSArray *)dataArr{
    if (_dataArr==nil) {
        NSString *path=[[NSBundle mainBundle]pathForResource:@"apps.plist" ofType:nil];
        NSArray *array=[NSArray arrayWithContentsOfFile:path];
        NSMutableArray *arrM=[NSMutableArray array];
        for (NSDictionary *dict in array) {
            //把从数组中取出来的字典包装成模型
            WJCellItem *item=[WJCellItem cellItemWithDict:dict];
            [arrM addObject:item];
        }
        _dataArr=arrM;
    }
    return _dataArr;
}
//懒加载创建字典
-(NSMutableDictionary *)operations{
    if (_operations==nil) {
        _operations=[NSMutableDictionary dictionary];
    }
    return _operations;
}
//懒加载创建队列
-(NSOperationQueue *)queue{
    if (_queue==nil) {
        _queue=[[NSOperationQueue alloc]init];
    }
    return _queue;
}
//懒加载创建NSCache
-(NSCache *)images{
    if (_images==nil) {
        _images=[[NSCache alloc]init];
        //最多缓存100张图片
        _images.countLimit=100;
    }
    return _images;
}

//整个tableView只有一组
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}
//每一组有多少行
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.dataArr.count;
}
//每一行cell的内容
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    //设置cell的标示
    static NSString *ID=@"cell";
    //tableViwe的重用机制,先从缓冲池里找有没有对应标示的cell
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:ID];
    //如果在缓存池里找不到再来创建
    if (cell==nil) {
        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];

    }
    //取出模型,给cell设置数据
    WJCellItem *item=self.dataArr[indexPath.row];
    cell.textLabel.text=item.name;
    cell.detailTextLabel.text=item.download;
    //由于plist文件中给的是图片的url,所以需要我们去下载
    //先去内存缓存中去找,如果内存缓存中有就直接取出来设置
    UIImage *image=[self.images objectForKey:item.icon];
    if (image) {
        cell.imageView.image=image;
        //如果内存缓存中没有就去沙盒(磁盘)里找,如果沙盒(磁盘)中有,就从沙盒(磁盘)中取出来设置,并且把图片保存到内存中
    }else{
        //获取沙盒路径
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        //获取图片路径的最后一个节点
        NSString *imagePath=[item.icon lastPathComponent];
        //拼接路径获取图片的全路径
        NSString *fullPath=[caches stringByAppendingPathComponent:imagePath];
        //从磁盘中试着取数据
        NSData *data=[NSData dataWithContentsOfFile:fullPath];
        if (data) {
            UIImage *image=[UIImage imageWithData:data];
            cell.imageView.image=image;
            //如果磁盘中也没有找到图片数据,我们就先去操作字典中查找有没有这个图片的下载操作,如果有就等待下载完毕,如果没有就需要我们手动下载
        }else{

            //在图片下载完成之前设置一张占位图片,以防止cell重用引发的图片错乱问题
            cell.imageView.image=[UIImage imageNamed:@"xcode"];
            //试着去存储操作的字典里找当前的下载操作
            NSBlockOperation *download=[self.operations objectForKey:item.icon];
            //如果在字典中没有找到该操作,就需要我们手动下载了,由于下载图片是耗时操作,因此需要开启子线程下载
            if (download==nil) {
                //封装操作
                NSBlockOperation *download=[NSBlockOperation blockOperationWithBlock:^{
                    //获取图片的url
                    NSURL *url=[NSURL URLWithString:item.icon];
                    //根据图片的url将图片的二进制数据下载到本地
                    NSData *data=[NSData dataWithContentsOfURL:url];
                    //根据二进制数据生成一张图片
                    UIImage *image=[UIImage imageWithData:data];
                    //如果没有得到图片,直接返回,防止在设置图片的时候程序崩溃
                    if (image==nil) {
                        return ;
                    }
                    //将图片保存一份到内存中
                    [self.images setObject:image forKey:item.icon];
                    //将图片的二进制数据保存一份到磁盘中
                    [data writeToFile:fullPath atomically:YES];
                    //在主队列中设置图片
                    [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                        //刷新表格
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    }];

                }];
                //将操作添加到队列中
                [self.queue addOperation:download];
                //将下载操作添加到缓存中一份,防止重复创建同一个操作
                [self.operations setObject:download forKey:item.icon];
            }else{
                //当在存储操作的字典里找到了当前的操作就会来到这个方法
                //在这里面不需要做任何操作,只需要等着图片加载完毕后显示即可
            }

        }

    }
    //返回当前的cell
    return cell;
}
//发生内存警告时的处理
-(void)didReceiveMemoryWarning{
    //清除图片缓存
    [self.images removeAllObjects];
    //取消所有的任务
    [self.queue cancelAllOperations];
}
@end

模型如下:

#import 

@interface WJCellItem : NSObject
@property (nonatomic,copy)NSString *name;
@property (nonatomic,copy)NSString *icon;
@property (nonatomic,copy)NSString *download;

+(instancetype)cellItemWithDict:(NSDictionary*)dict;
@end

#import "WJCellItem.h"

@implementation WJCellItem
+(instancetype)cellItemWithDict:(NSDictionary *)dict{
    WJCellItem *item=[[self alloc]init];
    [item setValuesForKeysWithDictionary:dict];
    return item;
}
@end

NSCache缓存类拓展:

  • 在此项目中用到了NSCache缓存类,其实它的用法跟NSMutableDictionary类似,在AFN和SDWebImage框架中被使用来管理缓存。
  • 苹果官方解释NSCache在系统内存很低时,会自动释放对象,建议:接收到内存警告时主动调用removeAllObject方法释放对象
  • NSCache是线程安全的,在多线程操作中,不需要对NSCache加锁
  • NSCache只是对Key进行Strong引用,不是拷贝

你可能感兴趣的:(iOS干货-->图片二级缓存机制(简仿SDWebImage内部实现))