在iOS的圖片加載框架中尚揣,SDWebImage使用頻率非常高。它支持從網(wǎng)絡(luò)中下載且緩存圖片守屉,并設(shè)置圖片到對(duì)應(yīng)的UIImageView控件或者UIButton控件惑艇。在項(xiàng)目中使用SDWebImage來(lái)管理圖片加載相關(guān)操作可以極大地提高開發(fā)效率,讓我們更加專注于業(yè)務(wù)邏輯實(shí)現(xiàn)。本文講解的版本為4.4.2版本滨巴。
文章目錄結(jié)構(gòu)
一 SDWebImage概論
二 SDWebImage組織架構(gòu)
三 各個(gè)類詳解
一 SDWebImage 概論
SDWebImage是個(gè)支持異步下載與緩存的UIImageView擴(kuò)展思灌。項(xiàng)目主要提供了一下功能:
1.提供了一個(gè)UIImageView的category用來(lái)加載網(wǎng)絡(luò)圖片并且對(duì)網(wǎng)絡(luò)圖片的緩存進(jìn)行管理
2.采用異步方式來(lái)下載網(wǎng)絡(luò)圖片
3.采用異步方式,使用內(nèi)存+磁盤來(lái)緩存網(wǎng)絡(luò)圖片恭取,擁有自動(dòng)的緩存過(guò)期處理機(jī)制泰偿。
4.支持GIF動(dòng)畫
5.支持WebP格式
6.同一個(gè)URL的網(wǎng)絡(luò)圖片不會(huì)被重復(fù)下載
7.失效,虛假的URL不會(huì)被無(wú)限重試
8.耗時(shí)操作都在子線程蜈垮,確保不會(huì)阻塞主線程
9.使用GCD和ARC
10.支持Arm64
11.支持后臺(tái)圖片解壓縮處理
12.項(xiàng)目支持的圖片格式包括 PNG,JPEG,GIF,Webp等
二 SDWebImage組織架構(gòu)
關(guān)鍵類講解
SDWebImageDownloader
:負(fù)責(zé)維持圖片的下載隊(duì)列耗跛;
SDWebImageDownloaderOperation
:負(fù)責(zé)真正的圖片下載請(qǐng)求;
SDImageCache
:負(fù)責(zé)圖片的緩存攒发;
SDWebImageManager
:是總的管理類调塌,維護(hù)了一個(gè)SDWebImageDownloader實(shí)例和一個(gè)SDImageCache實(shí)例,是下載與緩存的橋梁;
SDWebImageDecoder
:負(fù)責(zé)圖片的解壓縮惠猿;
SDWebImagePrefetcher
:負(fù)責(zé)圖片的預(yù)雀崂;
UIImageView+WebCache
:和其他的擴(kuò)展都是與用戶直接打交道的偶妖。
其中姜凄,最重要的三個(gè)類就是SDWebImageDownloader
、SDImageCache
趾访、SDWebImageManager
态秧。接下來(lái)我們就分別詳細(xì)地研究一下這些類各自具體做了哪些事,又是怎么做的扼鞋。
為了便于大家從宏觀上有個(gè)把握申鱼,這里先給出項(xiàng)目的框架結(jié)構(gòu):
- UIImageView+WebCache和UIButton+WebCache直接為表層的 UIKit框架提供接口
- SDWebImageManger負(fù)責(zé)處理和協(xié)調(diào)SDWebImageDownloader和SDWebImageCache, 并與 UIKit層進(jìn)行交互。
- SDWebImageDownloaderOperation真正執(zhí)行下載請(qǐng)求云头;最底層的兩個(gè)類為高層抽象提供支持润讥。
三 各個(gè)類詳解
我們按照上圖中從上到下執(zhí)行的流程來(lái)研究各個(gè)類
3.1 UIImageView+WebCache
這里只用UIImageView+WebCache
來(lái)舉個(gè)例子,其他的擴(kuò)展類似盘寡。
使用場(chǎng)景:已知圖片的url地址,下載圖片并設(shè)置到UIImageView上撮慨。
UIImageView+WebCache提供了一系列的接口:
- (void)sd_setImageWithURL:(nullable NSURL *)url;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options;
- (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock;
這些接口最終都會(huì)調(diào)用
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
新版本還給UIView增加了分類竿痰,即UIView+WebCache
,最終上述方法會(huì)走到下面的方法去具體操作砌溺,比如下載圖片等影涉。
- (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;
接下來(lái)對(duì)該方法進(jìn)行解析
- 第一步:取消當(dāng)前正在進(jìn)行的異步下載,確保每個(gè) UIImageView 對(duì)象中永遠(yuǎn)只存在一個(gè) operation规伐,當(dāng)前只允許一個(gè)圖片網(wǎng)絡(luò)請(qǐng)求蟹倾,該 operation 負(fù)責(zé)從緩存中獲取 image 或者是重新下載 image。具體執(zhí)行代碼是:
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
// 取消先前下載的任務(wù)
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
... // 下載圖片操作
// 將生成的加載操作賦值給UIView的自定義屬性
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
上述方法定義在UIView+WebCacheOperation
類中
- (void)sd_setImageLoadOperation:(nullable id<SDWebImageOperation>)operation forKey:(nullable NSString *)key {
if (key) {
// 如果之前已經(jīng)有過(guò)該圖片的下載操作,則取消之前的圖片下載操作
[self sd_cancelImageLoadOperationWithKey:key];
if (operation) {
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
@synchronized (self) {
[operationDictionary setObject:operation forKey:key];
}
}
}
}
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
// Cancel in progress downloader from queue
SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; // 獲取添加在UIView的自定義屬性
id<SDWebImageOperation> operation;
@synchronized (self) {
operation = [operationDictionary objectForKey:key];
}
if (operation) {
// 實(shí)現(xiàn)了SDWebImageOperation的協(xié)議
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
}
@synchronized (self) {
[operationDictionary removeObjectForKey:key];
}
}
}
}
實(shí)際上,所有的操作都是由一個(gè)實(shí)際上鲜棠,所有的操作都是由一個(gè)operationDictionary
字典維護(hù)的,執(zhí)行新的操作之前肌厨,cancel所有的operation。
- 第二步:占位圖策略
作為圖片下載完成之前的替代圖片豁陆。dispatch_main_async_safe
是一個(gè)宏柑爸,保證在主線程安全執(zhí)行。
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
// 設(shè)置占位圖
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
- 第三步:判斷url是否合法
如果url合法盒音,則進(jìn)行圖片下載操作表鳍,否則直接block回調(diào)失敗
if (url) {
// 下載圖片操作
} else {
dispatch_main_async_safe(^{
#if SD_UIKIT
[self sd_removeActivityIndicator];
#endif
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
- 第四步 下載圖片操作
下載圖片的操作是由SDWebImageManager
完成的,它是一個(gè)單例
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
下載完成之后刷新UIImageView的圖片祥诽。
// 根據(jù)枚舉類型譬圣,判斷是否需要設(shè)置圖片
shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
(!image && !(options & SDWebImageDelayPlaceholder)));
SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
if (!sself) { return; }
if (!shouldNotSetImage) {
[sself sd_setNeedsLayout]; // 設(shè)置圖片
}
if (completedBlock && shouldCallCompletedBlock) {
completedBlock(image, error, cacheType, url);
}
};
if (shouldNotSetImage) { // 不要自動(dòng)設(shè)置圖片,則調(diào)用block傳入image對(duì)象
dispatch_main_async_safe(callCompletedBlockClojure);
return;
}
// 設(shè)置圖片操作
dispatch_main_async_safe(^{
#if SD_UIKIT || SD_MAC
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
#else
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
#endif
callCompletedBlockClojure();
});
最后,把返回的id operation添加到operationDictionary中雄坪,方便后續(xù)的cancel厘熟。
// 將生成的加載操作賦值給UIView的自定義屬性
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
3.2 SDWebImageManager
在SDWebImageManager.h中是這樣描述SDWebImageManager類的:
/**
* The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.
* It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).
* You can use this class directly to benefit from web image downloading with caching in another context than
* a UIView.
*/
即隱藏在UIImageView+WebCache背后,用于處理異步下載和圖片緩存的類诸衔,當(dāng)然你也可以直接使用 SDWebImageManager 的方法 來(lái)直接下載圖片盯漂。
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
SDWebImageManager.h首先定義了一些枚舉類型的SDWebImageOptions
。
然后笨农,聲明了四個(gè)block:
//操作完成的回調(diào)就缆,被上層的擴(kuò)展調(diào)用。
typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);
//被SDWebImageManager調(diào)用谒亦。如果使用了SDWebImageProgressiveDownload標(biāo)記竭宰,這個(gè)block可能會(huì)被重復(fù)調(diào)用,直到圖片完全下載結(jié)束份招,finished=true,再最后調(diào)用一次這個(gè)block切揭。
typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
//SDWebImageManager每次把URL轉(zhuǎn)換為cache key的時(shí)候調(diào)用,可以刪除一些image URL中的動(dòng)態(tài)部分锁摔。
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);
typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);
定義了SDWebImageManagerDelegate協(xié)議:
@protocol SDWebImageManagerDelegate
@optional
// 控制在cache中沒有找到image時(shí) 是否應(yīng)該去下載廓旬。
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
// 在下載之后,緩存之前轉(zhuǎn)換圖片谐腰。在全局隊(duì)列中操作孕豹,不阻塞主線程
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;
@end
SDWebImageManager是單例使用的,分別維護(hù)了一個(gè)SDImageCache實(shí)例和一個(gè)SDWebImageDownloader實(shí)例十气。 對(duì)象方法分別是:
//初始化SDWebImageManager單例励背,在init方法中已經(jīng)初始化了cache單例和downloader單例。
- (instancetype)initWithCache:(SDImageCache *)cache downloader:(SDWebImageDownloader *)downloader;
//下載圖片
- (id )downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;
//緩存給定URL的圖片
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;
//取消當(dāng)前所有的操作
- (void)cancelAll;
//監(jiān)測(cè)當(dāng)前是否有進(jìn)行中的操作
- (BOOL)isRunning;
//監(jiān)測(cè)圖片是否在緩存中砸西, 先在memory cache里面找 再到disk cache里面找
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
//監(jiān)測(cè)圖片是否緩存在disk里
- (BOOL)diskImageExistsForURL:(NSURL *)url;
//監(jiān)測(cè)圖片是否在緩存中,監(jiān)測(cè)結(jié)束后調(diào)用completionBlock
- (void)cachedImageExistsForURL:(NSURL *)url
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
//監(jiān)測(cè)圖片是否緩存在disk里,監(jiān)測(cè)結(jié)束后調(diào)用completionBlock
- (void)diskImageExistsForURL:(NSURL *)url
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
//返回給定URL的cache key
- (NSString *)cacheKeyForURL:(NSURL *)url;
我們主要研究
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
- 首先叶眉,監(jiān)測(cè)url 的合法性:
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
第一個(gè)判斷條件是防止很多用戶直接傳遞NSString作為NSURL導(dǎo)致的錯(cuò)誤址儒,第二個(gè)判斷條件防止crash。
2.集合failedURLs保存之前失敗的urls衅疙,如果url為空或者url之前失敗過(guò)且不采用重試策略莲趣,直接調(diào)用completedBlock返回錯(cuò)誤。
BOOL isFailedUrl = NO;
if (url) { // 判斷url是否是失敗過(guò)的url
LOCK(self.failedURLsLock);
isFailedUrl = [self.failedURLs containsObject:url];
UNLOCK(self.failedURLsLock);
}
// 如果url為空或者url下載失敗并且設(shè)置了不再重試
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
return operation;
}
3.保存操作
LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
UNLOCK(self.runningOperationsLock);
runningOperations是一個(gè)可變數(shù)組炼蛤,保存所有的operation妖爷,主要用來(lái)監(jiān)測(cè)是否有operation在執(zhí)行,即判斷running 狀態(tài)理朋。
4.查找緩存
SDWebImageManager會(huì)首先在memory以及disk的cache中查找是否下載過(guò)相同的照片絮识,即調(diào)用imageCache的下面方法
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
如果操作取消,則直接返回
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (!strongOperation || strongOperation.isCancelled) { // operation取消,那么將下載任務(wù)從下載隊(duì)列中直接移除
[self safelyRemoveOperationFromRunning:strongOperation];
return;
}
如果沒有在緩存中找到圖片嗽上,或者不管是否找到圖片次舌,只要operation有SDWebImageRefreshCached
標(biāo)記,那么若SDWebImageManagerDelegate的shouldDownloadImageForURL方法返回true兽愤,即允許下載時(shí)彼念,都使用 imageDownloader 的下載方法
- (id )downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock
如果下載有錯(cuò)誤,直接調(diào)用completedBlock返回錯(cuò)誤浅萧,并且視情況將url添加到failedURLs里面逐沙;
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
}
});
if (error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
如果下載成功,若支持失敗重試洼畅,將url從failURLs里刪除:
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
如果delegate實(shí)現(xiàn)了吩案,imageManager:transformDownloadedImage:withURL:
方法,圖片在緩存之前帝簇,需要做轉(zhuǎn)換(在全局隊(duì)列中調(diào)用徘郭,不阻塞主線程)。轉(zhuǎn)化成功切下載全部結(jié)束丧肴,圖片存入緩存残揉,調(diào)用completedBlock回調(diào),第一個(gè)參數(shù)是轉(zhuǎn)換后的image芋浮。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
//將圖片緩存起來(lái)
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
});
否則抱环,直接存入緩存,調(diào)用completedBlock回調(diào)纸巷,第一個(gè)參數(shù)是下載的原始image江醇。
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
存入緩存都是調(diào)用imageCache的下面方法
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk
如果沒有在緩存找到圖片,且不允許下載何暇,直接調(diào)用completedBlock,第一個(gè)參數(shù)為nil凛驮。
dispatch_main_sync_safe(^{
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (strongOperation && !weakOperation.isCancelled) {//為啥這里用weakOperation TODO
completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
}
});
最后都要將這個(gè)operation從runningOperations里刪除裆站。
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
3.3 SDWebImageCombinedOperation
@interface SDWebImageCombinedOperation : NSObject
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic) NSOperation *cacheOperation;
@end
是一個(gè)遵循SDWebImageOperation協(xié)議的NSObject子類。
@protocol SDWebImageOperation
- (void)cancel;
@end
在里面封裝一個(gè)NSOperation,這么做的目的應(yīng)該是為了使代碼更簡(jiǎn)潔宏胯。因?yàn)橄螺d操作需要查詢緩存的operation和實(shí)際下載的operation羽嫡,這個(gè)類的cancel方法可以同時(shí)cancel兩個(gè)operation,同時(shí)還可以維護(hù)一個(gè)狀態(tài)cancelled肩袍。
SDWebImage 使用
1.使用IImageView+WebCache category來(lái)加載UITableView中cell的圖片
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://img1.cache.netease.com/catchpic/5/51/5132C377F99EEEE927697E62C26DDFB1.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
2.使用Blocks,采用這個(gè)方案可以在網(wǎng)絡(luò)圖片加載過(guò)程中得知圖片的下載進(jìn)度和圖片加載成功與否
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://img1.cache.netease.com/catchpic/5/51/5132C377F99EEEE927697E62C26DDFB1.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
//... completion code here ...
}];
3.使用SDWebImageManager,SDWebImageManager為UIImageView+WebCache category的實(shí)現(xiàn)提供接口杭棵。
SDWebImageManager *manager = [SDWebImageManager sharedManager] ;
[manager downloadImageWithURL:imageURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
// progression tracking code
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
}
}];
4.加載圖片還有使用SDWebImageDownloader和SDImageCache方式
5.key的來(lái)源
// 利用Image的URL生成一個(gè)緩存時(shí)需要的key.
// 這里有兩種情況,第一種是如果檢測(cè)到cacheKeyFilter不為空時(shí),利用cacheKeyFilter來(lái)處理URL生成一個(gè)key.
// 如果為空,那么直接返回URL的string內(nèi)容,當(dāng)做key.
- (NSString *)cacheKeyForURL:(NSURL *)url {
if (self.cacheKeyFilter) {
return self.cacheKeyFilter(url);
}
else {
return [url absoluteString];
}
}
SDWebImage 流程
SDWebImage 接口
SDWebImage是一個(gè)成熟而且比較龐大的框架,但是在使用過(guò)程中并不需要太多的接口,這算是一種代碼封裝程度的體現(xiàn)氛赐。這里就介紹比較常用的幾個(gè)接口魂爪。
1.給UIImageView設(shè)置圖片的接口,SDWebImage有提供多個(gè)給UIImageView設(shè)置圖片的接口艰管,最終所有的接口都會(huì)調(diào)用下圖的這個(gè)接口滓侍,這是大多數(shù)框架的做法。
///所以設(shè)置圖片的方法最終都會(huì)調(diào)用該方法
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
}
2.獲取SDWebImage的磁盤緩存大小,在項(xiàng)目中有時(shí)候會(huì)需要統(tǒng)計(jì)應(yīng)用的磁盤緩存內(nèi)容大小牲芋,那么獲取圖片的緩存大小就是使用這個(gè)接口來(lái)實(shí)現(xiàn)
[SDImageCache sharedImageCache] getSize];
3.清理內(nèi)存緩存撩笆,清理內(nèi)存中緩存的圖片資源,釋放內(nèi)存資源缸浦。
[[SDImageCache sharedImageCache] clearMemory];
4.有了清理內(nèi)存緩存夕冲,自然也有清理磁盤緩存的接口
[[SDImageCache sharedImageCache] clearDisk];
SDWebImage 解析
<1>入口 setImageWithURL:placeholderImage:options: 會(huì)先把 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片裂逐。
<2>進(jìn)入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:歹鱼,交給 SDImageCache 從緩存查找圖片是否已經(jīng)下載 queryDiskCacheForKey:delegate:userInfo:.
<3>先從內(nèi)存圖片緩存查找是否有圖片,如果內(nèi)存中已經(jīng)有圖片緩存絮姆,SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager醉冤。
<4>SDWebImageManagerDelegate 回調(diào) webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示圖片。
<5>如果內(nèi)存緩存中沒有篙悯,生成 NSInvocationOperation 添加到隊(duì)列開始從硬盤查找圖片是否已經(jīng)緩存蚁阳。
<6>根據(jù) URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進(jìn)行的操作鸽照,所以回主線程進(jìn)行結(jié)果回調(diào) notifyDelegate:螺捐。
<7>如果上一操作從硬盤讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過(guò)小矮燎,會(huì)先清空內(nèi)存緩存)定血。SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo:。進(jìn)而回調(diào)展示圖片诞外。
<8>如果從硬盤緩存目錄讀取不到圖片澜沟,說(shuō)明所有緩存都不存在該圖片,需要下載圖片峡谊,回調(diào) imageCache:didNotFindImageForKey:userInfo:茫虽。
<9>共享或重新生成一個(gè)下載器 SDWebImageDownloader 開始下載圖片刊苍。
<10>圖片下載由 NSURLConnection 來(lái)做,實(shí)現(xiàn)相關(guān) delegate 來(lái)判斷圖片下載中濒析、下載完成和下載失敗正什。
<11>connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進(jìn)度加載效果。
<12>connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理号杏。
<13>圖片解碼處理在一個(gè) NSOperationQueue 完成婴氮,不會(huì)拖慢主線程 UI。如果有需要對(duì)下載的圖片進(jìn)行二次處理盾致,最好也在這里完成主经,效率會(huì)好很多。
<14>在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成绰上,imageDecoder:didFinishDecodingImage:userInfo: 回調(diào)給 SDWebImageDownloader旨怠。
<15>imageDownloader:didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成。
<16>通知所有的 downloadDelegates 下載完成蜈块,回調(diào)給需要的地方展示圖片鉴腻。
<17>將圖片保存到 SDImageCache 中,內(nèi)存緩存和硬盤緩存同時(shí)保存百揭。寫文件到硬盤也在以單獨(dú) NSInvocationOperation 完成爽哎,避免拖慢主線程。
<18>SDImageCache 在初始化的時(shí)候會(huì)注冊(cè)一些消息通知器一,在內(nèi)存警告或退到后臺(tái)的時(shí)候清理內(nèi)存圖片緩存课锌,應(yīng)用結(jié)束的時(shí)候清理過(guò)期圖片。
<19>SDWebImage 也提供了 UIButton+WebCache 和MKAnnotationView+WebCache祈秕,方便使用渺贤。
<20>SDWebImagePrefetcher 可以預(yù)先下載圖片,方便后續(xù)使用请毛。
從上面流程可以看出志鞍,當(dāng)你調(diào)用setImageWithURL:方法的時(shí)候,他會(huì)自動(dòng)去給你干這么多事方仿,當(dāng)你需要在某一具體時(shí)刻做事情的時(shí)候固棚,你可以覆蓋這些方法。比如在下載某個(gè)圖片的過(guò)程中要響應(yīng)一個(gè)事件仙蚜,就覆蓋這個(gè)方法:
//覆蓋方法此洲,指哪打哪,這個(gè)方法是下載imagePath2的時(shí)候響應(yīng)
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imagePath2 options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"顯示當(dāng)前進(jìn)度");
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
NSLog(@"下載完成");
}];
簡(jiǎn)書常用格式
- 如有錯(cuò)誤委粉,歡迎指正呜师,多多點(diǎn)贊,打賞更佳贾节,您的支持是我寫作的動(dòng)力匣掸。