主要圍繞三個方面來闡述git
雙緩存
(磁盤 + 內存)下面根這源碼來驗證一下上面說的知識 根據 Github 上面提供的整個 SD 的類圖可知,主要是 SDWebImageManager
在操做管理查找流程. 咱們就根據你們使用的最多的方法, 來一步步跟蹤.github
UIImageView+WebCache.h 文件中
-(void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
複製代碼
sd_internalSetImageWithURL
方法.UIView+WebCache.h 文件中
sd_internalSetImageWithURL ....
複製代碼
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
});
}
複製代碼
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) {
複製代碼
callCacheProcessForOperation
進行緩存查找操做BOOL shouldQueryCache = (options & SDWebImageFromLoaderOnly) == 0;
if (shouldQueryCache) {
// 緩存查找
}else {
// 進行下載操做
}
複製代碼
SDImageCache
中的 queryImageForKey
方法繼續緩存查找-(id<SDWebImageOperation>)queryImageForKey:(NSString *)key options:(SDWebImageOptions)options context:(nullable SDWebImageContext *)context completion:(nullable SDImageCacheQueryCompletionBlock)completionBlock {
複製代碼
接下來到 queryCacheOperationForKey
方法緩存
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
複製代碼
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
複製代碼
MemoryCache
中,這樣就能夠再下次查找中更快的找到對應的圖片信息[self.memCache setObject:diskImage forKey:key cost:cost];
複製代碼
若是沒找到就去下載圖片bash
callDownloadProcessForOperation
複製代碼
SDWebImageDownloadToken
對象.就行下載[self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData finished:finished progress:progressBlock completed:completedBlock];
複製代碼
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
複製代碼
* 分別存儲到內存和磁盤
```
-(void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toMemory:(BOOL)toMemory
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
```
複製代碼
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
複製代碼
到此整個圖片加載流程就完了.網絡
下圖是最新的 SDWebImage 的文件結構,這裏主要看看 Cache 板塊session
SDDiskCache
和 SDMemoryCache
SDImageCacheConfig
* 定義了默認的磁盤緩存時間, 默認爲 1 周 `kDefaultCacheMaxDiskAge = 60 * 60 * 24 *7;`
* shouldDisableiCloud 是否使用 iCloud
* shouldCacheImagesInMemory 是否使用內存緩存
* shouldUseWeakMemoryCache 是否使用弱應用內存緩存
* shouldRemoveExpiredDataWhenEnterBackground 是否刪除超過日期的數據
* maxDiskAge 最大磁盤緩存週期爲 `1` 周
* maxDiskSize 最大磁盤緩存大小.默認爲 0,意味着沒有大小限制
* maxMemoryCost 內存緩存大小,默認爲 0,意味着沒有大小限制
* maxMemoryCount 內存緩存的最大數量,默認 0
* diskCacheExpireType 磁盤緩存失效的類型(用於刪除使用)
複製代碼
* `SDMemoryCache` 是繼承自 系統的 `NSCache`.
* 這個類主要是操做內存,刪除,保存的操做
* 首先註冊了內存警告通知 `UIApplicationDidReceiveMemoryWarningNotification`
* 保存
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
[super setObject:obj forKey:key cost:g];
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
if (key && obj) {
// Store weak cache
SD_LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
SD_UNLOCK(self.weakCacheLock);
}
}
Note: 這裏的保存方法是作了兩次保存操做
1. 使用 [Super setObject:obj forKey:key cost: g] 先給父類設置,也就是給系統的 NSCache 設置緩存
2. 而後在給當前緩存內的 `NSMapTable` 設置值
3. 這樣也就是拿空間換時間的操做,至關於在內存中是有兩份數據的,若是第一份沒有,就去拿第二份,這樣就保證了快速度的響應.
* 取值
-(id)objectForKey:(id)key {
id obj = [super objectForKey:key];
if (!self.config.shouldUseWeakMemoryCache) {
return obj;
}
if (key && !obj) {
// Check weak cache
SD_LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
SD_UNLOCK(self.weakCacheLock);
if (obj) {
// Sync cache
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
cost = [(UIImage *)obj sd_memoryCost];
}
[super setObject:obj forKey:key cost:cost];
}
}
return obj;
}
1. 先去 `super` 取值
2. 若是木有就去 `NSMapTable` 中取值
複製代碼
* 使用 NSFileManager 管理圖片緩存
* 根據 `key` 在它 SDDiskCacheFileNameForKey 方法中,使用 `CC_MD5` 生成一個 新的 `Key`, 轉換成 url,存放 data.
* 還有一點就是清理過時的數據,有兩種方式
1. SDImageCacheConfigExpireTypeAccessDate 根據訪問時間
2. SDImageCacheConfigExpireTypeModificationDate 修改時間(默認)
3. 根據 self.config.maxDiskAge 來對比刪除超過期間的圖片
4. 根據 self.config.maxDiskSize 來刪除磁盤緩存的數據,清理到self.config.maxDiskSize /2 爲止.
複製代碼
* 主要是操做 `SDImageCache` 類,對緩存就行 `存儲`, `刪除`, `清理`, `查詢`
複製代碼
* 主要是對圖片的 `Decode` 操做
複製代碼
* 值得說的是,他在這裏監聽了兩個系統通知
1. UIApplicationWillTerminateNotification
2. UIApplicationDidEnterBackgroundNotification
當這兩個方法執行時,實際上是作了一件事情,就是清理磁盤緩存
[self.diskCache removeExpiredData];
複製代碼
* 設置最大併發數 maxConcurrentDownloads ,默認是 6
* 設置下載超時時間 downloadTimeout ,默認 15s
* 下載執行順序 executionOrder , 默認先進先出
複製代碼
Note: 總結就是 使用 系統 NSURLSession 建立 task ,在本類中它實現了 NSURLSessionTaskDelegate, 當數據下載完成,就去顯示.
* 主要是設置下載隊列
* 拼接 HTTPHeader
* 組裝 SDWebImageDownloaderOperation
* 設置 Credential
* 配置 NSOperation 的優先級
* 返回一個 SDWebImageDownloaderOperation
* 雖然設置了 NSURLSessionTaskDelegate 的代理在這個類中,可是實際仍是把結果交費給了 SDWebImageDownloaderOperation, 統一處理
NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:dataTask];
if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
} else {
if (completionHandler) {
completionHandler(NSURLSessionResponseAllow);
}
}
複製代碼
Note: 他是集成自 NSOperation, 重寫了 Start 方法
* 在 Start 時,若是 app 進入後臺,就取消下載.
* 設置 NSURLSession 以後就下載任務
* 其中手動(KVC)出發了 isFinished isExecuting
* 而後就是在 NSURLSessionTaskDelegate,NSURLSessionDataDelegate,設置顯示就回調
複製代碼