SDWebImage探索二 自己实现轻量级图片缓存

简介

通过对SDWebImage源码的阅读,就尝试着自己封装一个轻量级的图片加载类。那么在开发的过程中遇到了几个问题

  1. UITableView上加载图片怎么做到不卡贞的
  2. 如何解决图片重用问题的
  3. NSOperation 是如何使用的
  4. 架构层面该如何设计
    下面我们就带着这些问题来一点一点写代码
声明一个UIImageView 的类别

UIImageView+XBWebCache

- (void)xb_setImageWithUrl:(NSString *)url{
    self.image = nil; 
    // 取消下载操作
    [[XBImageManager sharedDown] cancleDownloadWithUrl:self.urlStr];
    // 记录当前的下载操作
    self.urlStr = url;
    [[XBImageManager sharedDown] downloadImageWithUrl:url complete:^(NSData * _Nonnull data, NSString * _Nonnull url,UIImage *image) {
        self.image = image;
    }];
}
- (void)setUrlStr:(NSString *)urlStr{
    objc_setAssociatedObject(self, xb_loadimageUrlKey, urlStr, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)urlStr{
    return objc_getAssociatedObject(self, xb_loadimageUrlKey);
}

这里的 ** self.image = nil; ** 是解决重用第一处后边还有

XBImageManager

声明一个 XBImageManager 图片下载管理类 提出api供上层调用

核心代码
- (void)downloadImageWithUrl:(NSString *)url complete:(XBHandleBlock)completeHandle{
    NSData *cacheData = [[XBCacheManager shareManager] getImageDataWithUrl:url];
    if (cacheData){
        NSLog(@"---currentThread---%@",[NSThread currentThread]);
        completeHandle(cacheData,url,[UIImage imageWithData:cacheData]);
        return;
    }
    [self safeAddCompleteBlock:url completeBlock:completeHandle];
    // 操作已经存在 无需再执行 并且记录者回调
    if (self.operationDic[url] != nil){
        return;
    }
    __weak typeof(self) weakSelf = self;
    XBImageDownloader *downloadOperation = [[XBImageDownloader alloc]initWithImageUrl:url complete:^(NSString * _Nonnull url, NSData * _Nonnull data) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        UIImage *image = [UIImage imageWithData:data];
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            completeHandle(data,url,image);
            // 下载成功 删除operate
            [strongSelf safeRemoveOperate:url];
            // 下载成功 执行图片回调
            NSMutableArray *handleBlockArr = strongSelf.handleDic[url];
            for (XBHandleBlock block in handleBlockArr){
                block(data,url,image);
            }
            [[XBCacheManager shareManager] saveMemoryWithUrl:url urlData:data];
            [[XBCacheManager shareManager] saveToDiskWithUrl:url urlData:data];
            // 下载成功将保存的数据删除 节省点内存
            [self safeRemoveHandleBlock:url];
        }];
    }];
    [self.downloadQueue addOperation:downloadOperation];
    // 将正在执行的下载操作存储
    [self.operationDic setValue:downloadOperation forKey:url];
}
- (void)cancleDownloadWithUrl:(NSString *)url{
    @synchronized (self) {
        if (_operationDic[url] != nil){
            NSOperation *operate = _operationDic[url];
            [operate cancel];
            [_operationDic removeObjectForKey:url];
        };
    }
}

这里的 cancleDownloadWithUrl 是解决重用的第二处代码 这个取消很重要,对于UITableview来讲UIIMageView 都是重用的,如果当前划走的话 以前相同的一个图片的下载操作就完全没有必要了。

一个NSOperation的子类 XBImageDownloader

核心代码

- (void)start{
    if (_cancelled){
        return;
    }
    NSURL *url = [NSURL URLWithString:_reqUrl];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
    
    NSURLSessionDataTask *sessionTask = [session dataTaskWithRequest:request];
    [sessionTask resume];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
    [_mutData appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    if (!self.cancelled){
        _completeHandler(_reqUrl,[_mutData copy]);
    }
    [self reset];
}

if (!self.cancelled){
_completeHandler(_reqUrl,[_mutData copy]);
}
这里是解决重用的第三处 如果已经取消回调函数也没有必要执行
将NSOperation 加入到队列中会自动执行start方法这是根据 NSURLSession进行下载,此处跟AF 一毛一样。知识AF有加了一个identifier和delegate的映射关系,因为他要知道是哪个请求结束了 我们这里就简单的按照url来了。

声明一个缓存类 XBCacheManager

下面的方法就是模拟 SD的缓存策略。

+ (instancetype)shareManager{
    static XBCacheManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[XBCacheManager alloc]init];
    });
    return manager;
}
- (void)saveMemoryWithUrl:(NSString *)url urlData:(NSData *)data{
    [self.memoryCache setValue:data forKey:url];
}
- (void)saveToDiskWithUrl:(NSString *)url urlData:(NSData *)data{
    [url saveImageWithData:data];
}
- (NSData *)getImageDataWithUrl:(NSString *)url{
    if (self.memoryCache[url]){
        return self.memoryCache[url];
    }
    else {
        return [url getImageDataWithUrl];
    }
}
- (NSMutableDictionary *)memoryCache{
    if (!_memoryCache){
        _memoryCache = [[NSMutableDictionary alloc]initWithCapacity:0];
    }
    return _memoryCache;
}
总结

多看源码,并且自己尝试着封装一个出来 ,会再潜移默化中影响自己的实际开发,加油,为成为iOS高手而努力不懈。。详细代码看github https://github.com/everyStudyNow/XBImageCache

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值