經過SDWebImageManager來進行管理,主要模塊有三個:加載模塊、緩存模塊、下載模塊。 瀏覽器
先放一張加載流程圖: 緩存
從 UIView+WebCache.m中的sd_internalSetImageWithURL:placeholderImage:options:operationKey:setImageBlock:progress:completed 開始分析用來存儲着每個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來控制其生命週期