经过SDWebImageManager来进行管理,主要模块有三个:加载模块、缓存模块、下载模块。 浏览器
先放一张加载流程图: 缓存
用来存储着每个view对应的下载operationbash
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//取消对应的Operation
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self operationDictionary];
id operations = operationDictionary[key];
if (operations) {
if ([operations isKindOfClass:[NSArray class]]) {
for (id <SDWebImageOperation> operation in operations) {
if (operation) {
[operation cancel];
}
}
} else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
[(id<SDWebImageOperation>) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
复制代码
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
__strong __typeof (wself) sself = wself;
[sself sd_removeActivityIndicator];
if (!sself) {
return;
}
dispatch_main_async_safe(^{
if (!sself) {
return;
}
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
//下一个runloop刷新周期对当前view进行刷新
[sself sd_setNeedsLayout];
} else {
if ((options & SDWebImageDelayPlaceholder)) {
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
复制代码
这一步就会根据上面那张流程图那样:先去查找缓存——>再去进行下载——>分两步(UI显示和图片缓存),这里UI显示的时候会根据SDWebImageOptions来决定是否须要对图片进行一些处理,好比:async
/**
* 只进行内存缓存,不进行磁盘缓存
*/
SDWebImageCacheMemoryOnly = 1 << 2,
/**
* 这个标志能够渐进式下载,显示的图像是逐步在下载(就像你用浏览器浏览网页的时候那种图片下载,一截一截的显示
*/
SDWebImageProgressiveDownload = 1 << 3,
/**
* 在加载图片时加载占位图。 此标志将延迟加载占位符图像,直到图像完成加载。
*/
SDWebImageDelayPlaceholder = 1 << 9,
/**
* 图片在下载后被加载到imageView。可是在一些状况下,咱们想要设置一下图片(引用一个滤镜或者加入透入动画)
* 使用这个来手动的设置图片在下载图片成功后
*/
SDWebImageAvoidAutoSetImage = 1 << 11,
复制代码
等等,这里没有列举彻底,能够本身看源码枚举里的注释。oop
SDWebImage使用的是内存和磁盘双缓存。 两个类:SDImageCacheConfig:对当前缓存的配置 和 SDImageCache:具体的缓存查找性能
是否要压缩图片,默认YES: @property (assign, nonatomic) BOOL shouldDecompressImages; 压缩图片能够提升性能,但会消耗内存动画
是否须要内存缓存,默认YES @property (assign, nonatomic) BOOL shouldCacheImagesInMemory;ui
还有一些属性也不一一列举了,能够自行查看源码。atom
SDImageCache里包含了两个属性:url
//内存缓存
@property (strong, nonatomic, nonnull) NSCache *memCache;
//磁盘缓存
@property (strong, nonatomic, nonnull) NSString *diskCachePath;
复制代码
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
在收到内存警告的时候,进行缓存清理。
- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
return [paths[0] stringByAppendingPathComponent:fullNamespace];
}
复制代码
具体读取图片时的源码:
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
// 检查内存缓存
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
NSData *diskData = nil;
if ([image isGIF]) {
diskData = [self diskImageDataBySearchingAllPathsForKey:key];
}
if (doneBlock) {
doneBlock(image, diskData, SDImageCacheTypeMemory);
}
return nil;
}
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
// do not call the completion if cancelled
return;
}
@autoreleasepool {
//检查磁盘缓存
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
//磁盘缓存找到后缓存到内存
[self.memCache setObject:diskImage forKey:key cost:cost];
}
if (doneBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
});
}
}
});
return operation;
}
复制代码
一样是两个类SDWebImageDownloader和SDWebImageDownloaderOperation SDWebImageDownloader管理类:负责处理一些公共的信息,好比下载的相关参数、下载队列里的前后顺序、最大下载任务数量、请求头、参数的设置等等
//优先级枚举:
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
//先进先出 默认
SDWebImageDownloaderFIFOExecutionOrder,
//后进先出
SDWebImageDownloaderLIFOExecutionOrder
};
//设置优先级 后进先出
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder)
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
复制代码
SDWebImageDownloaderOperation:自定义NSOperation来控制其生命周期