设计一个简单的图片缓存器

作者: _Andy_ | 来源:发表于2017-03-15 10:53 被阅读942次
    • 某天看到技术群里 传了一份 知乎上的 如何面试 iOS 工程师?的面试题 里面有一题: 麻烦你设计个简单的图片内存缓存器(移除策略是一定要说的)。别的题不敢说,这题 用上篇文章的 ZBNetworking 就可以做到。
      上篇提到 ZBNetworking 优点1. 低耦合,易扩展。可能有的朋友没当回事。今天就用 ZBNetworking里面的ZBCacheManager 类去制作图片内存缓存器,(准确的说我的缓存是磁盘存储,并不是内存),但我感觉出题者就是再问 怎么设计个简单图片缓存器。其实加上内存也很简单用NSCache 在判断 存 取时 多加几个if而已。但是ZBNetworking已经设计时就定位了 磁盘存储。还是不改了。

    • 在我们开发维护的应用里 图片请求已经有非常多的优秀第三方框架了 SDWebImage,YYWebImage 等等 。但是在某个角落还是能经常看到下面的代码,一个系统自己的图片请求方法。制作简单的图片缓存器,我们就用简单的,如果我们给这样的代码加上缓存,那么是不是非常👍

            NSURL *url=[NSURL URLWithString:imageUrl];
            NSData *data=[NSData dataWithContentsOfURL:url];
            UIImage *image=[UIImage imageWithData:data];
    

    先写一个方法

    - (void)requestImageUrl:(NSString *)imageUrl completion:(downloadCompletion)completion{
        if (!imageUrl)return;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
           
            NSURL *url=[NSURL URLWithString:imageUrl];
            
            NSData *data=[NSData dataWithContentsOfURL:url];
            
            UIImage *image=[UIImage imageWithData:data];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                completion(image);
            });
        });
    }
    
    

    一个在子线程 的图片请求,并在后面有个block回调,返回一个 请求完成的UIimage.下面用ZBCacheManager进行存储

      [self requestImageUrl:imageUrl completion:^(UIImage *image){ 
           [[ZBCacheManager sharedInstance]storeContent:image forKey:imageUrl path:path];
       }];
    

    代码很简单 当我们请求完成了 把返回的UIimage进行存储到指定路径(要提前创建存储路径),

    - (void)downloadImageUrl:(NSString *)imageUrl path:(NSString *)path completion:(downloadCompletion)completion{
        
        if ([[ZBCacheManager sharedInstance]diskCacheExistsWithKey:imageUrl path:path]) {
            
            [[ZBCacheManager sharedInstance]getCacheDataForKey:imageUrl path:path value:^(NSData *data,NSString *filePath) {
                
                UIImage *image=[UIImage imageWithData:data];
                
                completion(image) ;
            }];
            
        }else{
            [self requestImageUrl:imageUrl completion:^(UIImage *image){
                
                [[ZBCacheManager sharedInstance]storeContent:image forKey:imageUrl path:path];
                
                completion(image);
            }];
        }
    }
    
    

    和ZBNetworking 一样的思路,有缓存就返回缓存,没缓存就进行请求,并存储。下面是适配不同类型的方法

    - (void)downloadImageUrl:(NSString *)imageUrl{
        [self downloadImageUrl:imageUrl completion:nil];
    }
    
    - (void)downloadImageUrl:(NSString *)imageUrl completion:(downloadCompletion)completion{
        [self downloadImageUrl:imageUrl path:[self imageFilePath] completion:completion];
    }
    
    - (void)downloadImageUrl:(NSString *)imageUrl path:(NSString *)path{
        [self downloadImageUrl:imageUrl path:path completion:nil];
    }
    
    - (void)downloadImageUrl:(NSString *)imageUrl path:(NSString *)path completion:(downloadCompletion)completion{
        
        if ([[ZBCacheManager sharedInstance]diskCacheExistsWithKey:imageUrl path:path]) {
            
            [[ZBCacheManager sharedInstance]getCacheDataForKey:imageUrl path:path value:^(NSData *data,NSString *filePath) {
                
                UIImage *image=[UIImage imageWithData:data];
                
                completion(image) ;
            }];
            
        }else{
            [self requestImageUrl:imageUrl completion:^(UIImage *image){
                
                [[ZBCacheManager sharedInstance]storeContent:image forKey:imageUrl path:path];
                
                completion(image);
            }];
        }
    }
     
    //存储路径 默认缓存路径  /Library/Caches/ZBKit/AppImage
    - (NSString *)imageFilePath{
        NSString *AppImagePath =  [[[ZBCacheManager sharedInstance]ZBKitPath]stringByAppendingPathComponent:ImageDefaultPath];
        return AppImagePath;
    }
    

    可能是个人习惯吧,总是喜欢自定义路径。所以除了可以存储到默认路径,还可以存到你想存的路径。

    下面是显示缓存大小/个数 ,删除缓存的操作

    //大小
    - (NSUInteger)imageFileSize{
        return [[ZBCacheManager sharedInstance]getFileSizeWithpath:[self imageFilePath]];
    }
    //数量 
    - (NSUInteger)imageFileCount{
        return [[ZBCacheManager sharedInstance]getFileCountWithpath:[self imageFilePath]];
    }
    //清除图片
    - (void)clearImageFile{
        [self clearImageFileCompletion:nil];
    }
    //清除图片缓存 有block回调
    - (void)clearImageFileCompletion:(ZBCacheCompletedBlock)completion{
        [[ZBCacheManager sharedInstance]clearDiskWithpath:[self imageFilePath] completion:completion];
    }
    //清除某个图片
    - (void)clearImageForkey:(NSString *)key{
        [self clearImageForkey:key completion:nil];
    }
    //清除某个图片缓存 有block回调
    - (void)clearImageForkey:(NSString *)key completion:(ZBCacheCompletedBlock)completion{
        [[ZBCacheManager sharedInstance]clearCacheForkey:key path:[self imageFilePath] completion:completion];
    }
    
    

    使用时这样使用,很简单易懂

    self.imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 64, SCREEN_WIDTH, 240)];
    [self.view addSubview:self.imageView];
     //下载图片
     [[ZBWebImageManager sharedInstance]  downloadImageUrl:imageUrl completion:^(UIImage *image){
            self.imageView.image=image;
     }];
    
    • 制作 UIImageView category.直接上代码
    - (void)zb_setImageWithURL:(NSString *)urlString{
        [self zb_setImageWithURL:urlString completion:nil];
    }
    
    - (void)zb_setImageWithURL:(NSString *)urlString placeholderImage:(UIImage *)placeholder{
        [self zb_setImageWithURL:urlString placeholderImage:placeholder path:nil];
    }
    
    - (void)zb_setImageWithURL:(NSString *)urlString placeholderImage:(UIImage *)placeholder path:(NSString *)path{
        [self zb_setImageWithURL:urlString placeholderImage:placeholder path:path completion:nil];
    }
    
    - (void)zb_setImageWithURL:(NSString *)urlString completion:(downloadCompletion)completion{
        [self zb_setImageWithURL:urlString placeholderImage:nil completion:completion];
    }
    
    - (void)zb_setImageWithURL:(NSString *)urlString placeholderImage:(UIImage *)placeholder completion:(downloadCompletion)completion{
        [self zb_setImageWithURL:urlString placeholderImage:placeholder path:[[ZBWebImageManager sharedInstance]imageFilePath] completion:completion];
    }
    
    - (void)zb_setImageWithURL:(NSString *)urlString placeholderImage:(UIImage *)placeholder path:(NSString *)path completion:(downloadCompletion)completion{
        if(placeholder){
            self.image=placeholder;
        }
        __weak __typeof(self)wself = self;
        [[ZBWebImageManager sharedInstance]downloadImageUrl:urlString path:path completion:^(UIImage *image){
            if (image) {
                wself.image=image;
                [wself setNeedsLayout];
            }else{
                wself.image=placeholder;
                [wself setNeedsLayout];
            }
            if (completion) {
                completion(image);
            }
        }];
        
    }
    
    

    上面核心方法 很简单的判断 有占位图 先用占位图 ,拿到图片替换占位图 。
    使用

     [self.imageView zb_setImageWithURL:imageUrl placeholderImage:[UIImage imageNamed:@"zhanweitu"]];
    

    到此 一个简单图片缓存器就完事了
    但是不能代替其他第三方,因为图片还有很多其他要处理的。就要大家自己慢慢积累了,但是对开始的那个面试题,还是足够的。在实际项目中。正如前面所说当我们遇到这样的代码可以 用一下这个框架 。

            NSURL *url=[NSURL URLWithString:imageUrl];
            NSData *data=[NSData dataWithContentsOfURL:url];
            UIImage *image=[UIImage imageWithData:data];
    

    从此文章可以感觉到ZBNetworking 扩展性好 ZBCacheManager的强大吧,所有的存储判断都已经设计好了,只有你在该调用的时候调用就好了。

    点击github地址下载

    结尾:水平有限,代码也很烂,一直在努力学习中,大家多多包涵。如果你喜欢这个轮子,请给个star,这是对作者最大的鼓励和支持,拜谢!!!假如你有更好的想法或方案请留言!

    相关文章

      网友评论

      • PGOne爱吃饺子:你好,请问你的这个ZBCacheManager 缓存类,室内存缓存还是磁盘缓存
        _Andy_:磁盘缓存
      • PGOne爱吃饺子:ZBNetworking 是你写的一个网络请求的库么?
        丁桥人在外地:棒棒哒 上次我去面试也被问到如何设计一套图片下载工具
        PGOne爱吃饺子:@大苏Andi 好的,下次研究一下你的网络请求的库,不明白的地方给一下指导啊,谢谢啊
        _Andy_:是的 https://github.com/Suzhibin/ZBNetworking :smile:

      本文标题:设计一个简单的图片缓存器

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