多图下载代码

  • LZAppItem模型类
// LZAppItem.h
#import <Foundation/Foundation.h>

@interface LZAppItem : NSObject

/** 名称*/
@property (nonatomic ,strong) NSString *name;
/** 图标的地址*/
@property (nonatomic ,strong) NSString *icon;
/** 下载量*/
@property (nonatomic ,strong) NSString *download;

+(instancetype)appItemWithDict:(NSDictionary *)dict;

@end

// LZAppItem.m
#import "LZAppItem.h"

@implementation LZAppItem

+ (instancetype)appItemWithDict:(NSDictionary *)dict
{
    // 创建对象
    LZAppItem *appItem = [[LZAppItem alloc] init];
    // KVC
    [appItem setValuesForKeysWithDictionary:dict];
    // 返回对象
    return appItem;
}

@end
  • ViewController
// ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController


@end

// ViewController.m
#import "ViewController.h"
#import "LZAppItem.h"

@interface ViewController ()
/** 队列*/
@property (nonatomic ,strong) NSOperationQueue *queue;
/** 模型数组*/
@property (nonatomic, strong) NSArray *apps;
/** 图片缓存*/
@property (nonatomic ,strong) NSMutableDictionary *images;
/** 操作*/
@property (nonatomic ,strong) NSMutableDictionary *operations;
@end

@implementation ViewController

#pragma mark - 懒加载数据
- (NSArray *)apps
{
    if (_apps == nil) {
        // 1获取字典数组
        // 1.1获取路径
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];
        // 1.2获取到字典数组
        NSArray *dictArray = [NSArray arrayWithContentsOfFile:filePath];
        // 2.拿到模型数组
        // 2.1创建一个可变数组
        NSMutableArray *temp = [NSMutableArray array];
        // 2.2创建for循环
        for (NSDictionary *dict in dictArray) {
            LZAppItem *item = [LZAppItem appItemWithDict:dict];
            [temp addObject:item];
        }
        _apps = temp;
    }
    return _apps;
}

#pragma mark - 图片缓存
- (NSMutableDictionary *)images
{
    if (_images == nil) {
        _images = [NSMutableDictionary dictionary];
    }
    return _images;
}

#pragma mark - 队列
- (NSOperationQueue *)queue
{
    if (_queue == nil) {
        _queue = [[NSOperationQueue alloc] init];
        _queue.maxConcurrentOperationCount = 3;
    }
    return _queue;
}

#pragma mark - UITableViewDataSource方法
// 返回多少组
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

// 每组返回多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.apps.count;
}

// 每行显示什么内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"app";
    // 去缓存池里面找
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    // 拿到模型数据
    LZAppItem *item = self.apps[indexPath.row];
    // 赋值
    cell.textLabel.text = item.name;
    cell.detailTextLabel.text = item.download;

    // 赋值图片,重点

    // 检查缓存,用图片的地址做键
    UIImage *image = [self.images objectForKey:item.icon];
    if (image) { // 有内存缓存,即身上有钱
        // 直接赋值
        cell.imageView.image = image;
//        NSLog(@"%zd使用了内存缓存",indexPath.row);
    }else { // 没有内存缓存,即身上没有钱

        // 获取磁盘缓存路径
        // 0.0获取路径
        NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // 0.1得到图片的名称
        NSString *fileName = [item.icon lastPathComponent];
        // 0.2拼接文件的路径
        NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];

        // 去检查磁盘缓存,这里找到的是保存在cache里面的NSData数据
        NSData *imgData = [NSData dataWithContentsOfFile:fullPath];

        if (imgData) { // 如果磁盘缓存里面有该图片,磁盘缓存即是卡里面有钱
            // 拿到Image
            UIImage *image = [UIImage imageWithData:imgData];
            cell.imageView.image = image;

            // 把图片保存到身上,内存缓存
            [self.images setObject:image forKey:item.icon];

//            NSLog(@"%zd使用了磁盘缓存",indexPath.row);

        } else { // 如果磁盘缓存里面没有该图片,磁盘缓存即是卡里面没有钱
            // 来到这里,说明又没有内存缓存,又没有磁盘缓存
            // 清空图片或者是设置占位图片,目的是什么,因为cell是重复利用的,假设当你第一张图片显示完毕的时候,用户继续往下拖拽,下面的cell是由上面消失的cell重复利用过来的,而下面的cell去下载图片的时间可能比较长,所以显示的效果是上一张残留下来的图片,之后再把从网络下载的图片进行覆盖,也就是图片错乱了,所以,为了防止这个问题,用一张占位图片解决
            cell.imageView.image = [UIImage imageNamed:@"Snip20200808_172"];

            /*避免重复操作,当程序第一次运行起来的时候,显示第一个cell的时候,创建一个操作去服务器端获取数据,然后用户又随便往下拖拽,那么第一个cell就不再显示了,此时那个获取数据的操作还在执行,假设需要10秒,然后用户又随便拖拽,滚到了第一个cell,第一个cell又重新显示出来了,那么它还会继续创建一个操作去服务器端获取数据,那么可能就有好几个操作发送到服务器端去获取同一个数据了,没有必要,所以,这里采用了一个可变字典,用来判断,好办法,谁想出来的,牛逼*/
            // 检查操作缓存
            NSBlockOperation *dowbloadOperation = [self.operations objectForKey:item.icon];
            if (dowbloadOperation) { // 如果有操作,说明之前已经发了一次操作过去了,那么再次来到这的时候,就不能再发请求了,所以什么也不能做

            } else { // 如果没有操作,说明之前没有发过操作过去,要添加操作
                    dowbloadOperation = [NSBlockOperation blockOperationWithBlock:^{


                    // 1.创建url
                    NSURL *url = [NSURL URLWithString:item.icon];
                    // 2.拿到二进制数据
                    // 该方法通过url获取数据是有时间限制的,30秒,如果失败,返回nil
                    NSData *data = [NSData dataWithContentsOfURL:url];
                    // 3.转化为UIImage对象
                    UIImage *image = [UIImage imageWithData:data];
                    // 4.判断
                    if (image == nil) {
                        // 把这次操作删除掉
                        [self.operations removeObjectForKey:item.icon];
                        return ;
                    }
                    // 5.保存到内存缓存
                    [self.images setValue:image forKey:item.icon];
                    // 6.保存到磁盘缓存
                    [data writeToFile:fullPath atomically:YES];

                    NSLog(@"%zd直接下载",indexPath.row);

                    // 设置图片
                   // 下面这行代码应该放在主线程,那么应该回到主线程
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        // 下面这个block块里面的代码是在主线程执行的
                        // 如果没有刷新操作,那么不会显示图片,为什么,因为你虽然赋值了,但是没有
                        // 调用它的layoutsubviews创建尺寸,只有调用layoutsubviews才会显示出图片
                        // 也就是刷新,或者点击某一行的时候,它会触发layoutsubviews方法,那么才会
                        // 显示出图片了,但是,你用reloadData又没有必要,只需要设置某一张图片,你刷新
                        // 整个可视区域,那就傻逼了,所以,这里面最好的方式是,刷新特定的行数
//                        cell.imageView.image = image;
//                        [tableView reloadData];
                        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
                    }];

                }];

                // 添加操作到内存缓存中
                [self.operations setObject:dowbloadOperation forKey:item.icon];

                // 添加到队列
                [self.queue addOperation:dowbloadOperation];
            }

        }
    }

    // 返回cell
    return cell;
}

- (void)didReceiveMemoryWarning
{
    // 移除内存缓存
    [self.images removeAllObjects];
    // 取消队列中的操作
    [self.queue cancelAllOperations];
}
@end

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

推荐阅读更多精彩内容