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

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 <Foundation/Foundation.h>

@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引用,不是拷贝
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容