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

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

作者: xwf_code | 来源:发表于2016-10-23 09:18 被阅读0次

    使用第三方框架#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
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/lezhuttx.html