简介
通过对SDWebImage源码的阅读,就尝试着自己封装一个轻量级的图片加载类。那么在开发的过程中遇到了几个问题
- UITableView上加载图片怎么做到不卡贞的
- 如何解决图片重用问题的
- NSOperation 是如何使用的
- 架构层面该如何设计
下面我们就带着这些问题来一点一点写代码
声明一个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