SDWebImage源码解析
SDWebImage是通过类别的方式给UIImageview 和UIButton 等类进行扩展,方便用户下载图片并渲染到视图上的开源库,解读该开源代码的时候SDWebImage的版本是
3.7.5。
0x 01、 动态关联一个NSURL的属性到UIImageView,仅用于应用层需要返回sd_imageURL的时候读取,代码如下:
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
0x 02 、使用一个SDWebImageManager的单例.sharedManager开启一个SDWebImageCombinedOperation 操作下载,开启该下载之前,先去之前缓存过的下载失败的
NSMutableSet *failedURLs集合里去查找,当前要下载的url是否被包含,若被包含,则直接返回失败。
0x 03 、若0x 02没有返回,则把SDWebImageCombinedOperation加入到一个runningOperations的数组里,用[url
absoluteString]作为key值,去SDImageCache里查询。以key为关键字的图片。
1 [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType){}]
0x 04、在SDImageCache查询的时候,先去查询以key为关键字的图片是否存在内存缓中,若内存缓存中做存在,则返回查找成功。
1 UIImage *image = [self imageFromMemoryCacheForKey:key]; 2 if (image) { 3 doneBlock(image, SDImageCacheTypeMemory); 4 return nil; 5 }
0x 05、若内存缓存中没有找到,则到硬盘存储中去继续查询关键字为key的图片,在硬盘里查找的时候,启动一个GCD的串行队列_ioQueue查找,若没有查找到则返回查找失败,若查找到,先把查找到的图片缓存到内存中去,再返回。
关键代码:
1 // 此处创建一个串行的GCD队列查找硬盘中的图片 2 _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); 3 4 NSOperation *operation = [NSOperation new]; 5 dispatch_async(self.ioQueue, ^{ 6 if (operation.isCancelled) { 7 return; 8 } 9 10 @autoreleasepool { 11 UIImage *diskImage = [self diskImageForKey:key]; 12 //缓存查找到的图片到内存中去 13 if (diskImage && self.shouldCacheImagesInMemory) { 14 NSUInteger cost = SDCacheCostForImage(diskImage); 15 [self.memCache setObject:diskImage forKey:key cost:cost]; 16 } 17 //返回查找到的图片 18 dispatch_async(dispatch_get_main_queue(), ^{ 19 doneBlock(diskImage, SDImageCacheTypeDisk); 20 }); 21 } 22 });
0x 06、 若内存和硬盘中都没有查找到图片,则在SDWebImageDownloader里启动一个SDWebImageDownloaderOperation的
operation开始下载,SDWebImageDownloaderOperation类继承自NSOperation类,所以可以检查一下下载的时候是否是先进先出,如果不是,则把最后一次添加的
operation添加依赖到当前operation之后执行。
关键代码:
1 operation = [[wself.operationClass alloc] initWithRequest:request 2 options:options 3 progress:^(NSInteger receivedSize, NSInteger expectedSize) {}];//启动一个operation 任务 4 5 //把一个downloadQueue任务添加到一个NSOperationQueue里去 6 [wself.downloadQueue addOperation:operation]; 7 //若不是先进先出的任务,则添加前一个任务依赖后一个任务,一般情况下默认是先进先出,不用处理 8 if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { 9 // Emulate LIFO execution order by systematically adding new operations as last operation's dependency 10 [wself.lastAddedOperation addDependency:operation]; 11 wself.lastAddedOperation = operation; 12 }
0x 07、 在SDWebImageDownloaderOperation 内部启动一个NSURLConnection请求来下载图片。下载完成之后,解码图片,然后缓存到内存和硬盘中去(是否缓存到硬盘中去取决于用户设置的options选项)。
关键代码:
if (!image.images) { if (self.shouldDecompressImages) { image = [UIImage decodedImageWithImage:image]; } }//解码图片 //缓存图片到内存和硬盘中去。 [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
0x 08 返回图片给主线程,刷新视图控件。
补充一下,一般图片默认缓存在目录下(Library/Caches/default)
1 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 2 return [paths[0] stringByAppendingPathComponent:fullNamespace];