SDWebImage
先看一张官方图:

UIImageView ---->
UIImageView (WebCache)
[self sd_internalSetImageWithURL:url
placeholderImage:placeholder
options:options
operationKey:nil
setImageBlock:nil
progress:progressBlock
completed:completedBlock];
UIView (WebCache)
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock
context:(nullable NSDictionary<NSString *, id> *)context {
[self sd_cancelImageLoadOperationWithKey:validOperationKey]
取消当前UI空间的下载operation
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
拿到 [SDWebImageManager sharemanager]
去manager里面去loadImageWithURL查找缓存
SDWebImageManager
manager = [SDWebImageManager sharedManager];
---->[图片上传失败...(image-84f965-1586670613002)]
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock ;
SDMemoryCache <KeyType, ObjectType> ()
---->
去缓存查找,cache,disk
(nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock
通过SDImageCache 去查找缓存
key = url cache 通过 url为key(默认url为key,也可以自定义cacheKeyFilter block) 去查找缓存
disk 是通过url MD5之后加上路径去获取data 查找图片
[self imageFromMemoryCacheForKey:key]
[self defaultCachePathForKey:key]
key -> diskCachePath/[url md5]
/Users/tangyj/Library/Developer/CoreSimulator/Devices/EB0F9D20-942D-4321-9C85-7D0F6895F7DB/data/Containers/Data/Application/3EFE64DF-FC1A-4CB9-BF0C19F86295C861/Library/Caches/default/com.hackemist.SDWebImageCache.default/e8ef725f48e22570c3befecc91d75aa9.png
如果缓存没找到,或者options是SDWebImageRefreshCached类型,非SDWebImageFromCacheOnly
SDWebImageDownloader
核心代码 创建了session,添加了operation 到downloadQueue 中,downloadQueue的maxOperations = 6
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
[self.URLOperations setObject:operation forKey:url];
// Add operation to operation queue only after all configuration done according to Apple's doc.
// `addOperation:` does not synchronously execute the `operation.completionBlock` so this will not cause deadlock.
[self.downloadQueue addOperation:operation];
去下载SDWebImageDownloader *imageDownloader
[SDWebImageCombinedOperation new] --- cacheOperation / downloadToken
strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished);
[self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval];
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
}
如果NSURLSessionDataDelegate执行会偷传给SDWebImageDownloaderOperation 的代理方法。
//#pragma mark NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
}
SDWebImageDownloaderOperation : NSOperation
//self.callbackBlocks 里面存放progressBlock,completedBlock
SDCallbacksDictionary*callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
LOCK(self.callbacksLock);
[self.callbackBlocks addObject:callbacks];
UNLOCK(self.callbacksLock);
// 如果没有session创建session,一般download会传过来
if(!session){
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
self.ownedSession = session;
}
// 创建datatask
self.dataTask = [session dataTaskWithRequest:self.request];
//下载完成
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSArray<id> *completionBlocks = [self callbacksForKey:kCompletedCallbackKey];
completedBlock(image, imageData, error, finished);
}
@interface SDWebImageDownloadToken ()
@property (nonatomic, weak, nullable) NSOperation<SDWebImageDownloaderOperationInterface> *downloadOperation;
@end
这个类作为downloader 的返回值
SDImageCache 单例
/**
- Cache Config object - storing all kind of settings
*/
@property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
/**
- The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
*/
@property (assign, nonatomic) NSUInteger maxMemoryCost;
/**
- The maximum number of objects the cache should hold.
*/
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
里面主要有一个SDMemoryCache,继承NSCache,不过自身又存了一个maptable,
NSMapTable(strong key,weak value)的hashtable,NSCache本身存储不稳定,内存紧张的时候会自己释放对象,不大可控
@property (strong, nonatomic, nonnull) SDMemoryCache *memCache;
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType>
@interface SDMemoryCache <KeyType, ObjectType> ()
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache; // strong-weak cache
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe
@end
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
[super setObject:obj forKey:key cost:g];
if (key && obj) {
// Store weak cache
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
UNLOCK(self.weakCacheLock);
}
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deleteOldFiles)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundDeleteOldFiles)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
进入后台和将要推出 deleteOldFilesWithCompletionBlock
删除 比设定时间久的文件,默认是7天有效期
for (NSURL *fileURL in urlsToDelete) {
[self.fileManager removeItemAtURL:fileURL error:nil];
}
设置过预期maxCacheSize的,如果当前size大于预期,按照修改添加日期排序,从oldest开始删除,删除到预期maxCacheSize的一半为止。
if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self.fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
网友评论