SDWebImage
一、取消之前的下载操作
注:用一个 NSMapTable<NSString *, id>类型的属性保存下载操作,map 中一个 NSString 类型的key 对应一个 下载操作。
根据 key 查找到之前的 operation,然后取消这个 operation,再把这个operation从maptable中移除。
//生成key
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
//取消旧的下载操作
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
sd_cancelImageLoadOperationWithKey: 方法
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
// Cancel in progress downloader from queue
/*
//SDOperationsDictionary:typedef NSMapTable<NSString *, id<SDWebImageOperation>> SDOperationsDictionary;
*/
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
id<SDWebImageOperation> operation;
@synchronized (self) {
operation = [operationDictionary objectForKey:key];
}
if (operation) {
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
//取消
[operation cancel];
}
@synchronized (self) {
//移除
[operationDictionary removeObjectForKey:key];
}
}
}
}
二、设置占位图
如果option != SDWebImageDelayPlaceholder 设置占位图。
三、重置进度条
// reset the progress
self.sd_imageProgress.totalUnitCount = 0;
self.sd_imageProgress.completedUnitCount = 0;
四、加载图片
SDWebImageManager(单例) 负责根据 url 加载图片,首先调用 SDWebImageManager 中的 loadImageWithUrl: 方法加载图片,然后把当前加载图片的 operation 加入到 operationDictionary 中。
//manager(SDWebImageManager) 调用 loadImageWithUrl: 方法
id <SDWebImageOperation> operation = [manager loadImageWithURL:url options:options progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
//这里是设置图片相关的代码。
// ...
}
//把当前加载图片的 operation 加入到 operationDictionary 中
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
SDWebImageManager(loadImageWithUrl:)方法
SDWebImageManager 用一个 NSMutableSet 类型的属性 failedURLs 存储所有请求失败的URL,加载图片之前先判断当前 URL 是否在 failedURLs 中,如果不在 failedURLs 中,继续向下执行。
用一个 NSMutableSet(NSMutableSet<SDWebImageCombinedOperation *>)类型的属性 runningOperations 存放所有在执行的 operation。开始加载之前需要把 当前图片下载操作添加到 runningOperations 中。
LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);
- 根据图片 URL 生成key;
NSString *key = [self cacheKeyForURL:url];
- 根据 key 从本地查找图片;
SDImageCache 管理图片存取。调用 SDImageCache 的 queryCacheOperationForKey: 方法根据 key 从本地查找图片。
- 首先从内存中查找
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
- 从磁盘中查找
/从磁盘中查找
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
//如果查找到,将这个图片加入到内存中
[self.memCache setObject:diskImage forKey:key cost:cost];
- 下载图片
- 判断是否要下载图片:没有设置 SDWebImageFromCacheOnly(只从本地缓存中获取) && (没有从本地查找到当前 url 对应的图片 || 设置了 SDWebImageRefreshCached):
BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
&& (!cachedImage || options & SDWebImageRefreshCached)
&& (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
- 下载图片
如果 shouldDownload == YES,调用 SDWebImageDownloader 下载图片。
a.下载成功
如果设置 SDWebImageRetryFailed == YES(默认是YES), 需要把当前 URL 从 failedURLs 中移除。
if ((options & SDWebImageRetryFailed)) {
LOCK(self.failedURLsLock);
[self.failedURLs removeObject:url];
UNLOCK(self.failedURLsLock);
}
调用 SDImageCache 把下载好的图片添加内存中,如果没有设置 SDWebImageCacheMemoryOnly,磁盘中也会缓存一份。
//SDImageCache
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
//...
// if memory cache is enabled
if (self.config.shouldCacheImagesInMemory) {
...
//缓存到内存中,NSData
[self.memCache setObject:image forKey:key cost:cost];
}
if (toDisk) {//写入磁盘
//write to disk
}
//...
}
内存存储
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
//...
if (key && obj) {
// Store weak cache
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
/*
weakCache 属性:
NSMapTable<KeyType, ObjectType> *weakCache;
<NSString* key,NSData* image>
*/
UNLOCK(self.weakCacheLock);
}
}
磁盘存储
- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
//...
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// transform to NSUrl
//生成存储路径
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
//写入磁盘
[imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
//...
}
b.下载失败
如果下载失败,将当前请求失败的 URL 添加到failedURLsLock中
LOCK(self.failedURLsLock);
[self.failedURLs addObject:url];
UNLOCK(self.failedURLsLock);
下载图片的请求完成后将当前 operation 从 runningOperationsLock 中移除
LOCK(self.runningOperationsLock);
[self.runningOperations removeObject:operation];
UNLOCK(self.runningOperationsLock);
五、设置图片
加载完成后,在 loadImageWithUrl: 的回调 block 中更新进度条,设置图片。