多线程04 下载网络Json数据 理解异步下载图片处理沙盒内存线程

使用第三方框架#import "AFNetworking.h"下载网络Json数据

- (void)loadJSONData
{
    // 1.创建网络请求管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 2.使用网络请求管理者,发送网络请求获取json数据
    [manager GET:@"https://raw.githubusercontent.com/zhangxiaochuZXC/SZiOS07_FerverFile/master/apps.json" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        // responseObject : 发送请求需要获取的数据
        NSLog(@"%@-%@-%@",[responseObject class],responseObject,[NSThread currentThread]);
        
        // 下一步 : 实现字典转模型
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"%@",error);
    }];
}

使用第三方框架#import "YYModel.h"转换模型

第一个参数需要先创建模型 传入模型的类型
第二个数据需要传已经解析好的Json数据
NSArray *appList = [NSArray yy_modelArrayWithClass:[APPModel class] json:dictArr];

使用第三方框架"UIImageView+WebCache.h"(SDWebImage)下载网络图片

第一个参数传的是网络图片的地址
第二个参数传的是占位图 直接传入以图片对象即可

[imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] 
placeholderImage:[UIImage imageNamed:@"user_default"]];

练习模拟企业异步下载图片

需求 : 列表异步加载网络图片

"SDWebImage实现列表异步加载网络图片" : 一定要掌握

1.需求分析
1.1 UI / 界面分析 : 了解界面怎么布局 / 怎么实现 (使用哪些UI控件)
1.2 数据结构分析 : 了解数据结构是为了了解如何实现字典转模型(字典数组转模型数组 / 字典转模型)

2.获取要zha展示的json数据
2.0 https://raw.githubusercontent.com/zhangxiaochuZXC/SZiOS07_FerverFile/master/apps.json
2.1 拿到json数据对应的地址URL
2.2 使用AFN发送请求,获取json数据
2.3 AFN默认是在子线程异步获取网络数据,然后自动回到主线程调用success代码块
2.4 AFN默认帮我们实现了json的反序列化

3.YYModel实现字典数组转模型数组
3.1 拿到responseObject之后,需要使用YYModel实现字典数组转模型数组
self.appList = [NSArray yy_modelArrayWithClass:[APPModel class] json:dictArr];

3.2 字典数组转模型数组之后,得到数据源数组

4.刷新UI / 自定义cell
4.1 得到数据源数组之后,一定要调用

[self.tableView reloadData];

5.SDWebImage实现图片的下载和展示
5.1 SD这个框架是自动在子线程下载图片,回到主线程给图片空间赋值
5.2 在使用SD框架时,一定要设置展位图;不然,控件不会加载上去

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"user_default"]];

----------------------------------华丽的分割线-----------------------------------------

1.使用NSBlockOperation实现图片异步下载和在主线程刷洗UI
1.1 如果没有占位图,在图片下载结束之前,展示图片的位置,是一块空白,影响美观和用户体验
1.2 提示 : 将来做图片异步下载时,一定要记得先设置占位图

2.设置占位图
2.1 在建立异步的下载操作之前,先设置占位图;占位图放在项目的图片库

3.实现内存缓存
3.1 如果不实现内存缓存,cell上的图片每次展示之前都要从网络中加载
3.1 使用可变字典制作图片内存缓存池
3.2 图片下载完成之后,把非空的图片对象,添加到图片缓存池
3.4 在建立异步下载操作之前,判断要下载的图片在图片缓存池中有木有;如果有,就直接取出来展示;反之,就去下载;

4.解决图片错行的问题
4.1 当有网络延迟时,在cell上的图片还没有加载出来之前,来回的滚动cell,由于cell的复用机制,图片会错行展示
4.2 图片错行展示的本质 : 就是后面下载完成的图片,把前面下载完成的图片覆盖了
4.3 解决图片错行的办法 : 哪行cell上的图片下载完,就刷新哪行;拒绝使用重用的cell

5.解决重复建立下载操作的问题
5.1 当有网络延迟时,在cell上的图片还没有加载出来之前,来回的滚动cell,就会多次为同一张图片建立多个下载操作
5.2 解决办法 : 使用可变字典制作操作缓存池.在建立下载操作之前,判断要建立的下载操作,在缓存池里面有没有;如果有,就不在建立下载操作;反之,就建立下载操作
5.3 图片下载完成之后,不管下载的内容是否是空,都需要移除对应的下载操作

6.处理内存警告
6.1 目的 : 保证内存使用时的平衡

7.实现沙盒缓存
7.1 沙盒缓存只在程序重启时使用的
7.2 操作内存比操作沙盒效率高
7.3 在判断内存缓存之后,建立下载操作之前,判断沙盒缓存
7.4 程序重启后,如果沙盒里面有图片,就把沙盒数据再在内存中存一份

#import "ViewController.h"
#import "AFNetworking.h"
#import "YYModel.h"
#import "APPModel.h"
#import "APPCell.h"
#import "NSString+path.h"

@interface ViewController ()

/* 数据源数组 */
@property (strong, nonatomic) NSArray *appList;
/* 队列 */
@property (strong, nonatomic) NSOperationQueue *queue;
/* 图片缓存池 */
@property (strong, nonatomic) NSMutableDictionary *imagesCache;
/* 操作缓存池 */
@property (strong, nonatomic) NSMutableDictionary *OPCache;

@end

// 相当于后台人员提供给你的开发接口 : https://raw.githubusercontent.com/zhangxiaochuZXC/SZiOS07_FerverFile/master/apps.json
// 将来就是拿着这个URL去获取数据

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 实例化队列
    self.queue = [[NSOperationQueue alloc] init];
    // 实例化图片缓存池
    self.imagesCache = [[NSMutableDictionary alloc] init];
    // 实例化操作缓存池
    self.OPCache = [[NSMutableDictionary alloc] init];
    
    [self loadJSONData];
}

#pragma mark-获取json数据的主方法
- (void)loadJSONData
{
    // 1.创建网络请求管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 2.使用网络请求管理者,发送网络请求获取json数据;
    // GET方法默认是在子线程异步执行的,当AFN获取到网络数据之后,success回调是自动的默认在主线程执行的
    [manager GET:@"https://raw.githubusercontent.com/zhangxiaochuZXC/SZiOS07_FerverFile/master/apps.json" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        // responseObject就是字典数组 (AFN自动实现字典转模型)
        NSArray *dictArr = responseObject;
        // 实现字典转模型 : 字典数组转模型数组
        self.appList = [NSArray yy_modelArrayWithClass:[APPModel class] json:dictArr];
//        NSLog(@"%@",self.appList);
        
        // 拿到数据源数组之后,在主线程刷新UI,实现数据源方法
        [self.tableView reloadData];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"%@",error);
    }];
}

#pragma mark-实现数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.appList.count;
}

/*
 1.创建异步操作,在子线程下载图片,在主线程刷新UI
    问题 : 当图片没有下载完,控件是白板
    解决 : 在下载之前,设置占位图
 2.设置占位图
 3.实现内存缓存策略 (因为不做缓存,图片都是从网络中加载)
    解决 : 使用字典实现内存缓存,当要下载的图片在内存中已经有了,就不需要再建立下载操作了
 4.当有网络延迟时,来回滚动cell,由于cell的服用机制,就造成了图片的错位
    解决 : 刷新对应行
 5.当有网络延迟时,在cell上的图片还没有下载完之前,来回的滚动cell,会重复的建立下载操作
    解决 : 使用字典创建操作缓存池,每次建立下载操作之前,先判断要建立的下载操作有没有,如果有,就不在建立下载操作
 6.处理内存警告 : 目的就是为了控制内存平衡,保证程序不因为内存警告而闪退
 7.实现沙盒缓存 : 沙盒缓存只在程序重新启动时有用 / 需要把沙盒数据再在内存中存一份 : 操作内存的效率高于沙盒
 */

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    APPCell *cell = [tableView dequeueReusableCellWithIdentifier:@"APPCell" forIndexPath:indexPath];
    
    // 获取cell对应的模型数据
    APPModel *app = self.appList[indexPath.row];
    // 给cell的label子控件赋值
    cell.nameLabel.text = app.name;
    cell.downloadLabel.text = app.download;
    
    // 设置占位图
    cell.iconImageView.image = [UIImage imageNamed:@"user_default"];
    
    // 在建立下载操作之前,判断要下载的图片在图片缓存池中有没有,如果有,就直接读取,并展示
    UIImage *memImage = [self.imagesCache objectForKey:app.icon];
    if (memImage != nil) {
        NSLog(@"从内存中加载... %@",app.name);
        cell.iconImageView.image = memImage;
        return cell;
    }
    
    // 在建立下载操作之前,和判断内存缓存之后,判断是否有沙盒缓存;(提示 : 操作内存的效率高于沙盒)/
    // 沙盒缓存只在程序重新启动时有用
    UIImage *cacheImage = [UIImage imageWithContentsOfFile:[app.icon appendCaches]];
    if (cacheImage != nil) {
        NSLog(@"从沙盒中加载... %@",app.name);
        // 需要再在内存中存一份 : 操作内存的效率高于沙盒
        [self.imagesCache setObject:cacheImage forKey:app.icon];
        cell.iconImageView.image = cacheImage;
        return cell;
    }
    
    // 在建立下载操作之前,判断要建立的下载操作在操作缓存池里面有木有,如果有,就不在建立下载操作
    if ([self.OPCache objectForKey:app.icon] != nil) {
        NSLog(@"正在下载中... %@",app.name);
        return cell;
    }
    
#pragma mark-给cell的iconImageView赋值,先在子线程异步下载,再在主线程赋值
    
    // 创建异步操作
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"从网络中加载... %@",app.name);
        
        // 模拟网络延迟 : 实际开发中,每行cell的图片的大小有可能差别比较大,造成有些cell图片展示的比较快,有些比较慢,模拟[屏幕之外的cell延迟加载
//        if (indexPath.row > 9) {
//            [NSThread sleepForTimeInterval:60.0];
//        }
        
        // URL
        NSURL *URL = [NSURL URLWithString:app.icon];
        //data : 耗时的操作,最多执行60秒,60秒之后,如果图片没有下载下来,会返回nil
        NSData *data = [NSData dataWithContentsOfURL:URL];
        // image
        UIImage *image = [UIImage imageWithData:data];
        
        // 实现沙盒缓存 : 当image不为空时,才缓存到沙盒
        if (image != nil) {
            [data writeToFile:[app.icon appendCaches] atomically:YES];
        }
        
        // 当image下载完成之后,通知主线程刷新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
            // 图片下载完成之后要做的事情,
            if (image != nil) {
                // 把图片缓存到图片缓存池
                [self.imagesCache setObject:image forKey:app.icon];
                
                // 图片下载完成之后,刷新对应的行,会再次调用cellForRowAtIndexPath
                [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
            }
            
            // 当图片下载完成之后,把图片对应的下载操作,从操作缓存池移除,保证了内存平衡和保证了操作魂村吃里面只有没有执行完的操作
            [self.OPCache removeObjectForKey:app.icon];
        }];
    }];
    // 把下载操作添加到操作缓存池
    [self.OPCache setObject:op forKey:app.icon];
    // 把异步操作添加到队列
    [self.queue addOperation:op];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%tu",self.queue.operationCount);
}

// 当系统觉得内存不够用时,会发送内存警告的通知
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
    // 一定要清空图片缓存池
    [self.imagesCache removeAllObjects];
    // 可以清空操作缓存池
    [self.OPCache removeAllObjects];
    // 可以清空队列
    [self.queue cancelAllOperations];
}


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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 现在已经不是一个人单打独斗的时代,顺势而为,越来越多的人开始聚拢,志趣相投的一群人聚集在一起,他们相聚一起,开始谈...
    锦潇阅读 194评论 0 0
  • 今天的晨读我学到了什么? 哪个知识点可以用于改善我的工作或生活? 「知识诅咒」1:每次检查孩子的作业,会感叹如此简...
    花朵小恬阅读 122评论 7 8
  • 在这个寒假里一次偶然的机会独自一人去香港游玩了五天,这是一次前所未有的体验并且放下很多恐惧和担心,再一次对自己有了...
    梁意Athena阅读 2,090评论 3 8