SDWebImageClassDiagram.png
通过上面这张图我们可以得知
SDWebImage
中主要包含SDWebImageManager
、SDWebImageCache
、SDWebImageDownloader
以及UIView+WebCache
扩展类
-
SDWebImageManager
主要是对创建任务
、判断是否包含下载任务
、处理图片在本地
还是需要网络请求
逻辑(在loadImageWithURL
中进行实现) -
SDWebImageCache
主要是对图片生成的缓存
进行处理,包括保存图片数据
、清理磁盘
及图片缓存、查找
相关路径、确定所占的最大内存
等 -
SDWebImageDownloader
主要负责对公共信息的处理(请求头的拼接、block设置、参数初始化等)、对图片下载
进行处理,包含下载的最大并发数
、对下载任务
的相关操作以及反馈下载的进度及数据
等 -
UIView+WebCache
是一个扩展类
,主要是通过它将SDWebImageManager
中开放
的相关方法
进行调用然后反馈到UI界面
上
加载图片的流程
SDWebImageSequenceDiagram.png
从上图我们可以看出当调用SDWebImage
进行图片渲染的时候,步骤如下:
- 首先会通过
UIView+WebCache
扩展类就可以实现imageView
调用sd_setImageWithURL
方法 - 然后根据
sd_setImageWithURL
的具体实现去调用sd_internalSetImageWithURL
- 通过对任务的判断确保
任务
执行的先后顺序,然后去调用SDWebImageManager
开放的方法loadImageWithURL
- 这时就是我们熟悉的话语,先去查找缓存中是否存在,其中缓存分为
内存缓存
与磁盘缓存
,这一点SDWebImage
实现了双缓存-
内存缓存
是通过继承NSCache
去获得相关的缓存文件,重写以及补充一些方法对外开放,更容易去对内存进行管理
*磁盘缓存
是创建一个目录,并为每一个缓存文件
生成MD5文件名
,更容易去进行查找 - 如果缓存中存在对应的图片这时候就直接返回
image
- 如果缓存中不存在对应的图片数据则需要进行
网络请求
进行下载
,此时用到的是SDWebImageDownloader
中downloadImageWithURL
方法去进行下载 - 将下载下来的图片进行返回,并同时将
图片进行保存
以保证下载在调用时不需要进行网络请求
-
- 通过网络下载返回的图片或者磁盘中存在的图片进行渲染,最终实现
UI页面
上图片的显示
源码分析
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
context:(nullable SDWebImageContext *)context
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
...
//通过递归锁去实现 保证任务的并发数以及执行的先后顺序
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
...
//创建任务 通过返回的operation并将其存储在NSMapTable中,从其中查看其中任务队列
@weakify(self);
id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
@strongify(self);
if (!self) { return; }
// if the progress not been updated, mark it to complete state
if (imageProgress && finished && !error && imageProgress.totalUnitCount == 0 && imageProgress.completedUnitCount == 0) {
imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
}
上面是通过对指定任务的key值进行保存,以确定任务执行的先后顺序及相关任务状态
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
...
__weak typeof(strongOperation) weakSubOperation = strongOperation;
strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
//在此处是处理图片下载成功及失败的相关逻辑
如果任务下载失败则需要调用任务失败的相关方法,如果需要返回失败的图片地址,则通过信号量的方式确保同时只有一个任务执行
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
if (shouldBlockFailedURL) {
LOCK(self.failedURLsLock);
[self.failedURLs addObject:url];
UNLOCK(self.failedURLsLock);
}
//如果任务下载成功,则通过本身的block方法返回对应的image数据,并保存一份在缓存中,方便下次查找时能够迅速找到对应的文件。
@autoreleasepool {
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
NSData *cacheData;
// pass nil if the image was transformed, so we can recalculate the data from the image
if (self.cacheSerializer) {
cacheData = self.cacheSerializer(transformedImage, (imageWasTransformed ? nil : downloadedData), url);
} else {
cacheData = (imageWasTransformed ? nil : downloadedData);
}
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
}
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
上述代码清晰地展示了加载图片流程
- 先在缓存中进行查找,如果
缓存
中存在
则直接返回对应的image
- 如果缓存中没有,则需要通过
任务下载
的方式进行下载
,同时保存对应任务
的标识符
,保证同时只有一个
任务执行 - 如果图片
下载失败
,则需要调用通知
方法,方便及时返回失败的图片地址 - 如果图片
下载成功
,则需要将成功的image返回
,并同时在缓存中保存
一份,方便下次
进行查找时迅速找到