zoukankan      html  css  js  c++  java
  • 如何实现从网络获取图片的缓存机制

    前言

      在iOS开发中从网络加载图片是一个比较值得思考的问题,因为你要考虑用户的体验,这其实包括流畅度,以及用户的流量考虑,那么今天我就来简单的说点这方面知识。

    具体实现:

      说到缓存就可以分为内存缓存和沙盒缓存,内存缓存的话就是用简单的用一个字典来记录下载的图片。

      今天的环境就是从网络下载一些图片给tableview的imageView的image赋值,SAMApp是模型类,icon是url.

      1.定义几个属性,具体如下

    /** 所有数据 */
    @property (nonatomic, strong) NSArray *apps;
    
    /** 内存缓存的图片 */
    @property (nonatomic, strong) NSMutableDictionary *images;
    
    /** 记录正在下载的任务 */
    @property (nonatomic, strong) NSMutableDictionary *operations;
    
    /** 队列对象 */
    @property (nonatomic, strong) NSOperationQueue *queue;

      2.判断内存中有没有值,这里面把url当做key来从字典中取值,相当于内存中取值,如何有值那就直接给imageView的image赋值 

    // 从内存中取出图片
        UIImage *image = self.images[app.icon];
        
        if (image) {
            // 内存里面有值
            cell.imageView.image = image;

      

      3.如何内存中没有值,那么就去沙盒中检查有否有需要的图片,这里面把url当作最后的路径目录

    // 获取cache目录
            NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES) firstObject];
            
            // 获取文件名
            NSString *filePath = [app.icon lastPathComponent];
            
            // 计算出全部路径
            NSString *file = [cachePath stringByAppendingString:filePath];
            
            // 加载沙盒中的文件数据
            NSData *data = [NSData dataWithContentsOfFile:file];

      4.如果以上情况都没有,这时应该添加占位视图,如果你是自定义的cell可能不需要,但是系统的UITableViewCell是需要的,要不然需要拖动之后才会显示图片。

      5.这时我们就应该开启线程从网上下载图片,如果只是简单的开启线程下载,这个过程会出现图片的位置不对,搬动刷新之后才正常的状况,这是由于cell的重利用导致的,当图片正在下载的时候,你这个cell可能重利用到下一个位置上去了,然后正好下载结束就会产生错误的图片加载。如何解决呢?

      解决的方法其实很简单,因为我们下载的每一个图片我们都需要去开起一个NSOperation,这时我们用url为key的字典来记录这些正在下载的operation.每次先去这些operation里面取,如果有说明是正在下载,那么就不开起线程,如果没有在开起。

      没有的情况下,说明我们需要开起线程,在这种情况下我们需要注意一种情况,就是如何下载失败的情况下我们就要反这个key从字典里面移除,要不然这一个图片就会不能再下载。

    核心代码:

     if (image) {
            // 内存里面有值
            cell.imageView.image = image;
        } else {
            //从沙盒里面取
            // 获取cache目录
            NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES) firstObject];
            
            // 获取文件名
            NSString *filePath = [app.icon lastPathComponent];
            
            // 计算出全部路径
            NSString *file = [cachePath stringByAppendingString:filePath];
            
            // 加载沙盒中的文件数据
            NSData *data = [NSData dataWithContentsOfFile:file];
            // 沙盒里面是否有数据
            if (data) {
                // 有数据直接给图片赋值
                UIImage *image = [UIImage imageWithData:data];
                cell.imageView.image = image;
                
                // 重新存到内存里面
                self.images[app.icon] = image;
            } else {
                // 占位视图
                cell.imageView.image = [UIImage imageNamed:@"1"];
                
                NSOperation *operation = self.operations[app.icon];
                
                // 该任务是否正在下载
                if (operation == nil) {
                    
                    // 没有下载,创建
                    operation  =  [NSBlockOperation blockOperationWithBlock:^{
                        
                        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
                        
                        if (data == nil) {
                            
                            [self.operations removeObjectForKey:app.icon];
                            return ;
                        }
                        
                        UIImage *image = [UIImage imageWithData:data];
                        // 记录到内存字典
                        self.images[app.icon] = image;
                      
                        
                        // 回到主线程显示图片
                        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                            // 刷新指定行的数据
                            [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
                        }];
                        
                        // 把二进制文件写入沙盒
                        [data writeToFile:file atomically:YES];
                        
                        // 下载任务完成,移除记录的操作
                        [self.operations removeObjectForKey:app.icon];
                        
                    }];
                    
                    //加入队列
                    [self.queue addOperation:operation];
                    
                    // 记录正在下载的操作
                    self.operations[app.icon] = operation;
                }
            }
            
        }

      

      

  • 相关阅读:
    js简单工厂
    对象数组深浅拷贝
    分时函数的通用实现
    SQL技术内幕-4 row_number() over( partition by XX order by XX)的用法(区别于group by 和order by)
    SQL技术内幕-2
    SQL技术内幕-1
    js 阻止冒泡 兼容性方法
    C# 给数据库传入当前时间
    Ms sql server sql优化技巧
    SQl 字段中出现某一个词语的次数
  • 原文地址:https://www.cnblogs.com/samyangldora/p/4637833.html
Copyright © 2011-2022 走看看