1.首先使用SDWebImage才能進(jìn)行剖析(以UIImageView+WebCache.h為例)
附:由于有些操作需要在弱網(wǎng)下進(jìn)行,所以要使ios模擬器弱網(wǎng)工具用狱,地址為:https://download.developer.apple.com/Developer_Tools/Hardware_IO_Tools_for_Xcode_7.3
- (void)sd_setImageWithURL:(nullable NSURL*)url placeholderImage:(nullable UIImage*)placeholder
options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
?completed:(nullable SDExternalCompletionBlock)completedBlock
SDWebImageOptions:
SDWebImageRetryFailed:默認(rèn)情況下,如果一個(gè)url在下載的時(shí)候失敗了,那么這個(gè)url會被加入黑名單并且library不會嘗試再次下載,這個(gè)flag會阻止library把失敗的url加入黑名單(簡單來說如果選擇了這個(gè)flag,那么即使某個(gè)url下載失敗了,sdwebimage還是會嘗試再次下載他
SDWebImageLowPriority:默認(rèn)情況下,圖片會在交互發(fā)生的時(shí)候下載(例如你滑動tableview的時(shí)候),這個(gè)flag會禁止這個(gè)特性,導(dǎo)致的結(jié)果就是在scrollview減速的時(shí)候,才會開始下載(也就是你滑動的時(shí)候scrollview不下載,你手從屏幕上移走,scrollview開始減速的時(shí)候才會開始下載圖片
SDWebImageCacheMemoryOnly:這個(gè)flag禁止磁盤緩存,只有內(nèi)存緩存
SDWebImageProgressiveDownload:圖片會隨著下載一點(diǎn)點(diǎn)顯示
SDWebImageRefreshCached:一個(gè)圖片即使已經(jīng)緩存了,還是會根據(jù)url進(jìn)行重新請求.并且緩存策略依據(jù)NSURLCache而不是SDWebImage
SDWebImageContinueInBackground:啟動后臺下載,加入你進(jìn)入一個(gè)頁面,有一張圖片正在下載這時(shí)候你讓app進(jìn)入后臺,圖片還是會繼續(xù)下載(這個(gè)估計(jì)要開backgroundfetch才有用)
SDWebImageHandleCookies:可以控制存在NSHTTPCookieStore的cookies.
SDWebImageAllowInvalidSSLCertificates:允許不安全的SSL證書,在正式環(huán)境中慎用
SDWebImageHighPriority:默認(rèn)情況下,image在裝載的時(shí)候是按照他們在隊(duì)列中的順序裝載的(就是先進(jìn)先出).這個(gè)flag會把他們移動到隊(duì)列的前端,并且立刻裝載,而不是等到當(dāng)前隊(duì)列裝載的時(shí)候再裝載.
SDWebImageDelayPlaceholder:默認(rèn)情況下,占位圖會在圖片下載的時(shí)候顯示.這個(gè)flag開啟會延遲占位圖顯示的時(shí)間,等到圖片下載完成之后才會顯示占位圖.
SDWebImageTransformAnimatedImage:我們通常不會在動畫圖像上調(diào)用transformDownloadedImage委托方法,因?yàn)榇蠖鄶?shù)轉(zhuǎn)換代碼會破壞它隐孽,使用這個(gè)來轉(zhuǎn)換它們
SDWebImageAvoidAutoSetImage:圖片下載完成后手動設(shè)置圖片
SDWebImageScaleDownLargeImages:根據(jù)設(shè)備來縮放圖片
SDWebImageQueryDataWhenInMemory:默認(rèn)情況下侠草,當(dāng)圖像緩存在內(nèi)存中時(shí),我們不會查詢磁盤數(shù)據(jù)罩旋。 該掩碼可以強(qiáng)制同時(shí)查詢磁盤數(shù)據(jù)。
SDWebImageQueryDiskSync:默認(rèn)情況下眶诈,我們同步查詢內(nèi)存緩存涨醋,異步訪問磁盤緩存。該選項(xiàng)可以強(qiáng)制同步查詢磁盤緩存逝撬,以確保圖像在同一個(gè)runloop中加載
SDWebImageFromCacheOnly:僅僅從緩存加載浴骂,不從網(wǎng)絡(luò)下載
SDWebImageForceTransition:默認(rèn)情況下,當(dāng)圖像加載完成后使用`SDWebImageTransition`完成視圖轉(zhuǎn)換時(shí)宪潮,此轉(zhuǎn)換僅適用于從網(wǎng)絡(luò)下載圖像溯警。 這個(gè)掩碼也可以強(qiáng)制為內(nèi)存和磁盤緩存應(yīng)用視圖轉(zhuǎn)換
- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs ?從網(wǎng)絡(luò)下載圖片并顯示圖片動畫
工作流程:
UIButton和UIImage的使用的加載圖片方法都是調(diào)用UIView的方法;那么我們對UIView進(jìn)行剖析:
- (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 *)context
//validOperationKey用來標(biāo)識某一類別的下載操作狡相,如果傳入operationKey則使用它梯轻,如果沒有則使用類名
?NSString*validOperationKey = operationKey ?:NSStringFromClass([self class]);
//取消全部validOperationKey對應(yīng)的下載操作。為什么要取消:一個(gè)UIImageView要取消之前所有的下載這樣操作谣光,省時(shí)檩淋、省流量并且防止覆蓋,注意這是針對一個(gè)UIImageView對象萄金,這個(gè)步驟是取消一個(gè)UIImageView對象所有的下載蟀悦。
//UIView+WebCacheOperation將下載操作和validOperationKey綁定,它會給對象生成一個(gè)NSMapTable類型的對象SDOperationsDictionary<NSString *, id<SDWebImageOperation>來進(jìn)行綁定
?[self sd_cancelImageLoadOperationWithKey:validOperationKey];?
//自身生成一個(gè)屬性url氧敢,方便后面取消等操作
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//設(shè)置占位圖日戈,如果有動畫執(zhí)行完動畫再顯示占位圖
?dispatch_main_async_safe(^{
? ? ? ? ? ? [selfsd_setImage:placeholder imageData:nilbasedOnClassOrViaCustomSetImageBlock:setImageBlock];
? ? ? ? });
//加載圖片?SDWebImageManager *manager下載和緩存操作的管理類,是個(gè)單例孙乖;manager初始化的時(shí)候會初始化SDWebImageDownloader對象和SDImageCache對象浙炼;SDWebImageDownloader負(fù)責(zé)下載任務(wù)管理,SDWebImageDownloaderOperation對象負(fù)責(zé)下載操作唯袄;SDImageCache是圖片緩存類
//operation是SDWebImageCombinedOperation<SDWebImageOperation>類弯屈,它里面有一個(gè)SDWebImageDownloadToken類型的downloadToken屬性;downloadToken有一個(gè)url屬性恋拷,由于SDWebImageDownloader對象有一個(gè)字典屬性將url和SDWebImageDownloaderOperation關(guān)聯(lián)资厉,所以根據(jù)url可以取消相應(yīng)的操作。也就是根據(jù)validOperationKey找到operation蔬顾,operation包含downloadToken宴偿,downloadToken根據(jù)url取出DownloaderOperation來進(jìn)行取消,取消下載操作和回調(diào)并發(fā)送通知
? id operation = [manager loadImageWithURL:url????options:options????progress:combinedProgressBlock????completed:^(UIImage*image,NSData*data,NSError*error,SDImageCacheTypecacheType,BOOLfinished,NSURL*imageURL) { ?
}];
//將operation和validOperationKey關(guān)聯(lián)方便取消诀豁;當(dāng)上下滑動列表時(shí)由于cell的重用會導(dǎo)致同一個(gè)UIImageView多次下載窄刘,這樣就需要取消之前的下載
?[self sd_setImageLoadOperation:operation forKey:validOperationKey];
SDWebImageManager
加載圖片的核心是SDWebImageManager類
//加載圖片的核心方法
- (id)loadImageWithURL:(nullable NSURL*)url?options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock{
//operation可以取消
?SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
//SDImageCache從緩存中取圖片
operation.cacheOperation=[self.imageCache ? ? queryCacheOperationForKey:keyoptions:cacheOptions done:^(UIImage*cachedImage,NSData*cachedData,SDImageCacheTypecacheType){
//判斷是否該下載
?BOOL shouldDownload = (!(options &SDWebImageFromCacheOnly))
? ? ? ? ? ? && (!cachedImage || options &SDWebImageRefreshCached)
? ? ? ? ? ? && (![self.delegaterespondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegateimageManager:selfshouldDownloadImageForURL:url]);
}];
? if(shouldDownload) {
? ? ? ? //下載
? ?}
return ?operation;
}
SDWebImage的各個(gè)版本差異?
SDWebImage架構(gòu):
注意:SDWebImage最新版本SDWebImageDownloaderOperation類里面網(wǎng)絡(luò)下載已經(jīng)改為基于NSUrlSession來實(shí)現(xiàn)了舷胜,而不是基于NSURLConnection娩践。
雙向箭頭的意思是調(diào)用時(shí)從上往下調(diào)用,回調(diào)從下往上
SDWebImageDownloader
SD源碼中下載只涉及到兩個(gè)類烹骨,一個(gè)是SDWebImageDownloader翻伺,另外一個(gè)是SDWebImageDownloaderOperation。由文件命名不難看出展氓,SD采用的是NSOperation異步下載的方式穆趴。
SDWebImageDownloader是通過NSOperation來處理并發(fā)任務(wù),所以實(shí)現(xiàn)起來比較簡單遇汞。創(chuàng)建一個(gè)NSOperation未妹,并把它添加到Queue隊(duì)列即可
本地緩存:SDWebimage使用了NSCache作為內(nèi)存緩存
NSCache:
簡介:NSCache是蘋果官方提供的的緩存類,具體使用是和NSMutableDictionary很相似的空入,在AFNetworking和SDWebImage第三方框架中被用作管理緩存络它。
NSCache在系統(tǒng)內(nèi)存很低時(shí)會自動釋放對象;NSCache是線程安全的歪赢,在進(jìn)行多線程操作時(shí)化戳,不需要進(jìn)行加鎖;NSCache的Key只是對對象進(jìn)行了Strong引用,而非拷貝点楼;提供了可設(shè)置緩存的數(shù)目與內(nèi)存大小限制的方式扫尖。
屬性和方法說明:
1、屬性說明
// 緩存空間的最大成本掠廓,超出上限會自動回收對象换怖,默認(rèn)值是0,表示沒有限制蟀瞧。
@property NSUInteger totalCostLimit;
// 能夠緩存對象的最大數(shù)量沉颂。默認(rèn)值為0,表示沒有限制悦污。
@property NSUInteger countLimit;
// 標(biāo)識緩存是否回收廢棄的內(nèi)容铸屉。
@property BOOL evictsObjectsWithDiscardedContent;
2、方法說明
// 通過指定的鍵取出在緩存中存儲的數(shù)據(jù)切端。
- (nullable ObjectType)objectForKey:(KeyType)key;
// 在緩存中設(shè)置指定鍵名對應(yīng)的值彻坛,成本為0。
- (void)setObject:(ObjectType)obj forKey:(KeyType)key;?
// 在緩存中設(shè)置指定鍵名對應(yīng)的值帆赢,并且指定回收成本小压,以便進(jìn)行計(jì)算存儲在緩存中對象的總成本,當(dāng)出現(xiàn)內(nèi)存警告或者超出總成本時(shí)椰于,緩存就會進(jìn)行刪除部分元素的操作怠益。
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
// 通過指定的鍵清除在緩存中存儲的數(shù)據(jù)。
- (void)removeObjectForKey:(KeyType)key;
// 清除在緩存中存儲的所有數(shù)據(jù)瘾婿。
- (void)removeAllObjects;
實(shí)例:for(int?i?=?0?;?i?<?10?;?i++)?{
????????????NSString?*str?=?[NSString?stringWithFormat:@"在這里進(jìn)行了存儲數(shù)據(jù)"];
?????????????//?設(shè)置成本數(shù)為1
????????????[self.cache?setObject:str?forKey:@(i)?cost:1];
?????????????NSLog(@"存儲數(shù)據(jù)----%@",@(i));
??????????} //實(shí)際只能存儲5個(gè)數(shù)據(jù)
?NSString *str = [NSString stringWithFormat:@"在這里進(jìn)行了存儲數(shù)據(jù)"];
? ? ? ? for (inti =0; i <10; i++) {
? ? ? ? ? ? // 設(shè)置成本數(shù)為1
? ? ? ? ? ? [self.cache setObject:strforKey:@(i)cost:1];
? ? ? ? ? ? NSLog(@"存儲數(shù)據(jù)----%@",@(i));
? ? ? ? } //能存儲10個(gè)數(shù)據(jù)蜻牢,因?yàn)閏ache存儲的是指針,10個(gè)指針都一樣所以花銷為1
NSMapTable :NSDcitionary或者NSMutableDictionary中對于key和value的內(nèi)存管理是偏陪,對key進(jìn)行copy抢呆,對value進(jìn)行強(qiáng)引用,而NSMapTable對key和value是否要強(qiáng)引用還是弱引用可以讓使用者選擇如:
[NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory] ?//key和value都是弱引用
NSMapTableStrongMemory: __strong來存儲
NSPointerFunctionsWeakMemory: 用__weak來存儲
NSPointerFunctionsCopyIn:對key和value進(jìn)行copy處理
而關(guān)于Peronality的兩個(gè)選項(xiàng)是用來是用isEqual和hash比較標(biāo)準(zhǔn)
SDWebImage緩存細(xì)節(jié):
SDImageCache是圖片緩存類,它是一個(gè)單例笛谦,它的屬性memCache(SDMemoryCache類繼承自NSCache)抱虐,memCache負(fù)責(zé)存儲圖片,圖片解壓縮饥脑?
圖片緩存清理時(shí)機(jī):
當(dāng)系統(tǒng)發(fā)出內(nèi)存不足通知時(shí)恳邀,會將內(nèi)存中的所有圖片緩存都刪除掉。
當(dāng)程序進(jìn)入后臺時(shí)灶轰,會對磁盤的文件數(shù)據(jù)進(jìn)行清理谣沸。
當(dāng)收到程序關(guān)閉通知時(shí),會對磁盤中的文件數(shù)據(jù)進(jìn)行清理笋颤。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name:UIApplicationDidReceiveMemoryWarningNotification
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? object:nil];
? ? ? ? [[NSNotificationCenter defaultCenter] addObserver:self? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? selector:@selector(deleteOldFiles)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name:UIApplicationWillTerminateNotification
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? object:nil];
? ? ? ? [[NSNotificationCenter defaultCenter] addObserver:self? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? selector:@selector(backgroundDeleteOldFiles)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name:UIApplicationDidEnterBackgroundNotification
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? object:nil];
手動清理:[[SDImageCache?sharedImageCache]?clearMemory]; //從內(nèi)存中清理
[[SDImageCache sharedImageCache] clearDisk]; //從磁盤清理
磁盤緩存清理步驟 :
獲取磁盤中圖片的最后修改日期乳附。(為了減少磁盤和內(nèi)存數(shù)據(jù)交換,讀取是并不將整個(gè)文件讀入內(nèi)存,僅僅將文件的一些屬性讀入內(nèi)存中赋除,包括最后修改日期阱缓,該文件是否為文件夾,文件的大小和對應(yīng)文件的文件路徑)
根據(jù)最后修改日期將圖片進(jìn)行分類贤重,將那些已經(jīng)存放超過最長存放時(shí)間(默認(rèn)一周)的文件存儲在刪除數(shù)組茬祷,其他的文件信息存儲在另一個(gè)字典中清焕。并計(jì)算除去要?jiǎng)h除的文件之外的所有文件大小
根據(jù)刪除數(shù)組中的文件路徑并蝗,將對應(yīng)的文件刪除。
判斷剩下的文件大小是否超過用戶現(xiàn)在的磁盤最大容量秸妥。
如果超過滚停,則將剩余的文件進(jìn)行按修改時(shí)間進(jìn)行升序排列,然后刪除修改時(shí)間最早的文件粥惧,直到甚剩余文件大小小于最大磁盤容量的一半键畴。
SDWebImage播放gif文件:
歸根到底,播放gif其實(shí)是調(diào)用了系統(tǒng)提供的一個(gè)UIImage的類方法而已:
UIImage *animatedImage = [UIImage animatedImageWithImages:images duration:duration];
而SDWebimage把gif文件解碼成圖片數(shù)組
解碼過程:
NSData *data = [NSData dataWithContentsOfFile:path]; //把圖片以data的形式傳入
?CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);?
?size_t count = CGImageSourceGetCount(source); //count為gif圖片的數(shù)量
?for (size_ti =0; i < count; i++) {
? ? ? ? ? ? CGImageRefimageRef =CGImageSourceCreateImageAtIndex(source, i,NULL)
? ??????????? UIImage*image = [[UIImagealloc]initWithCGImage:imageRef]; //得到每一個(gè)圖片
? ? ? ? }
SDWebImage使用webP格式圖片:
由谷歌于2010年推出的新一代圖片格式突雪,在壓縮方面比當(dāng)前JPEG格式更優(yōu)越起惕,這種格式的主要優(yōu)勢在于高效率。他們發(fā)現(xiàn)咏删,“在質(zhì)量相同的情況下惹想,WebP格式圖像的體積要比JPEG格式圖像小40%;美中不足的是督函,WebP格式圖像的編碼時(shí)間“比JPEG格式圖像長8倍”嘀粱。
WebP 在各大互聯(lián)網(wǎng)公司已經(jīng)使用得很多了,國外的有 Google辰狡,F(xiàn)acebook 和 ebay锋叨,國內(nèi)的有淘寶、騰訊和美團(tuán)等
https://pic.zhaoxi.net/ ?//在線圖片格式
使用在線圖片格式將一張png圖片(原圖:139kb)宛篇,轉(zhuǎn)換成webP格式(33kb),用crome瀏覽器打開兩張圖幾乎沒有差別娃磺,看以看出webP格式的優(yōu)勢
使用YYImage:#import "YYImage.h"
? NSString *path = [[NSBundle mainBundle]pathForResource:@"20181022" ofType:@"webp"];
? ? NSData *data = [NSData dataWithContentsOfFile:path];
? ? YYImage*image = [YYImage imageWithData:data]; //加載webP圖片
? ? imageV.image= image;
或者使用SDWebImage加載WebP圖片:
核心類是UIImage+WebP.h和SDWebImageWebPCoder.h,可以使用YYImage的webp.framework,然后使用方式如下
?NSString *paht = [[NSBundle mainBundle]pathForResource:@"20181022" ofType:@"webp"];
? NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:paht]];
?UIImage*iamge =? [UIImagesd_imageWithWebPData:data];
使用SDWebImage遇到的坑
在使用sd_setImageWithURL下載圖片的時(shí)候叫倍,由于SDWebImage緩存機(jī)制使用的url的MD5判斷是否有緩存偷卧,如果我們使用- (void)sd_setImageWithURL:(NSURL *)url completed:(SDWebImageCompletionBlock)completedBlock來下載圖片,后臺更新了圖片段标,客戶端是拿不到這個(gè)新的圖片的涯冠。解決方案有兩種:
1、自己后臺在更新圖片的時(shí)候順便把圖片的url也更新一下
2逼庞、調(diào)用帶有SDWebImageOptions參數(shù)的方法比如- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options傳SDWebImageRefreshCached參數(shù)