iOS学习笔记-123.多线程22——多图下载2_第二种实现方式(三级缓存)

多线程22——多图下载2_第二种实现方式(三级缓存)

一、分析第一种方式的不足

iOS学习笔记-122.多线程21——多图下载1_第一种实现方式(不靠谱)

中,我们实现的方式存在严重不足。

  1. 使用了主线程去下载图片,使其UI卡顿

  2. 图片重复下载。

针对上面的问题我们解决方式如下

针对问题1,我们可以使用多线程来下载

针对问题2,我们可以使用三级缓存


二、三级缓存

2.1 什么是三级缓存

三级缓存就是

网络缓存, 不优先加载, 速度慢,浪费流量

本地缓存, 次优先加载, 速度快

内存缓存, 优先加载, 速度最快

2.2 三级缓存的工作原理

  1. 首先从内存中获取我们需要的资源,是否存在。如果存在,那么就是使用资> 源,如果不存在进行步骤2

  2. 从本地(磁盘)中获取我们需要的资源, > 是否存在。如果存在,那么就是使用资源,如果不存在进行步骤3

  3. 从网络中获取我们的资源来使用。


三、多图下载三级缓存分析

流程如图:

这里写图片描述

  1. 根据url我们生成对一个的图片唯一标示符,作为图片的名字。

  2. 从内存中判断我们的图片已经存在了,如果存在直接拿来显示。如果不存在继续步骤3

  3. 从本地(磁盘)中判断图片是否已经存在,如果存在直接拿来显示。如果不存在继续步骤4

  4. 判断我们的操作缓存中,如果存在那么不处理(子线程下载完成以后,会通知到主线程上来),如果不存在,那么添加下载操作到队列中和操作缓存中。

  5. 图片下载好以后,把图片添加到内存和本地缓冲中,把操作从操作缓存中移除。添加实现图片的任务到主线程中。

注意:处理上面,我们应该还要处理内存不足的情况,这个时候,我们移除内存中的图片和操作。


四、代码


//
//  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"

@interface ViewController ()
/** tableView的数据源 */
@property (nonatomic, strong) NSArray *apps;
/** 内存缓存 */
@property (nonatomic, strong) NSMutableDictionary *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;
}

-(NSMutableDictionary*)images{
    if(_images==nil){
        _images = [NSMutableDictionary dictionary];
    }
    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];
//    [self handle1:cell withItem:appItem];

    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;
}


/*
 图片的第一种处理方式,存在的问题
 1.UI很不流程 ------> 开启子线程下载
 2.图片重复下载 ----->先把之前已经下载的图片保存起来(字典)

 */
-(UITableViewCell*)handle1:(UITableViewCell*)cell withItem:(QWMAppItem*)appItem{
    NSURL *url = [NSURL URLWithString:appItem.icon];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    cell.imageView.image = image;
    return cell;
}

/*内存警告*/
-(void)didReceiveMemoryWarning{
    //内存不存的时候删除我们的缓存
    [self.operations removeAllObjects];
    [self.images removeAllObjects];
}

@end


五、图示

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值