一屡谐、功能簡(jiǎn)介:
1.一個(gè)添加了web圖片加載和緩存管理的UIImageView分類
2.一個(gè)異步圖片下載器
3.一個(gè)異步的內(nèi)存加磁盤綜合存儲(chǔ)圖片并且自動(dòng)處理過期圖片
4.支持動(dòng)態(tài)gif圖
5.支持webP格式的圖片
5.后臺(tái)圖片解壓處理
6.確保同樣的圖片url不會(huì)下載多次
7.確保偽造的圖片url不會(huì)重復(fù)嘗試下載
8.確保主線程不會(huì)阻塞
二娘纷、SDWebImage 加載圖片的流程
1.入口 setImageWithURL:placeholderImage:options:
會(huì)先把 placeholderImage
顯示,然后 SDWebImageManager
根據(jù) URL
開始處理圖片
2.進(jìn)入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:
,交給 SDImageCache
從緩存查找圖片是否已經(jīng)下載 queryDiskCacheForKey:delegate:userInfo:
- 先從內(nèi)存圖片緩存查找,如果內(nèi)存中已經(jīng)有圖片緩存,
SDImageCacheDelegate
回調(diào)imageCache:didFindImage:forKey:userInfo:
到SDWebImageManager
-
SDWebImageManagerDelegate
回調(diào)webImageManager:didFinishWithImage:
到UIImageView+WebCache
等前端展示圖片 - 如果內(nèi)存緩存中沒有,生成
NSInvocationOperation
添加到隊(duì)列開始從硬盤查找圖片是否已經(jīng)緩存
3.根據(jù) URLKey
在硬盤緩存目錄下嘗試讀取圖片文件因妙。這一步是在 NSOperation
進(jìn)行的操作,所以回主線程進(jìn)行結(jié)果回調(diào) notifyDelegate:
如果上一操作從硬盤讀取到了圖片票髓,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過小攀涵,會(huì)先清空內(nèi)存緩存)。SDImageCacheDelegate
回調(diào) imageCache:didFindImage:forKey:userInfo:
洽沟;進(jìn)而回調(diào)展示圖片
4.如果從硬盤緩存目錄讀取不到圖片以故,說明所有緩存都不存在該圖片,需要下載圖片玲躯,回調(diào) imageCache:didNotFindImageForKey:userInfo:
5.共享或重新生成一個(gè)下載器 SDWebImageDownloader
開始下載圖片
6.connectionDidFinishLoading:
數(shù)據(jù)下載完成后交給 SDWebImageDecoder
做圖片解碼處理
7.圖片解碼處理在一個(gè) NSOperationQueue
完成据德,不會(huì)拖慢主線程 UI。如果有需要對(duì)下載的圖片進(jìn)行二次處理跷车,可以在這里完成棘利,效率會(huì)好很多。
8.在主線程 notifyDelegateOnMainThreadWithInfo:
宣告解碼完成朽缴,imageDecoder:didFinishDecodingImage:userInfo:
回調(diào)給 SDWebImageDownloader
imageDownloader:didFinishWithImage:
回調(diào)給 SDWebImageManager
告知圖片下載完成
8.通知所有的 downloadDelegates
下載完成善玫,回調(diào)給需要的地方展示圖片
9.將圖片保存到 SDImageCache
中,內(nèi)存緩存和硬盤緩存同時(shí)保存密强。寫文件到硬盤也在以單獨(dú) NSInvocationOperation
完成茅郎,避免拖慢主線程
三、圖片緩存策略: (不緩存或渤,內(nèi)存緩存系冗,沙盒緩存)
SDImageCache是怎么做數(shù)據(jù)管理的?
SDImageCache分兩個(gè)部分薪鹦,一個(gè)是內(nèi)存層面的掌敬,一個(gè)是硬盤層面的惯豆。
內(nèi)存層面的相當(dāng)是個(gè)緩存器,以Key-Value的形式存儲(chǔ)圖片奔害。當(dāng)內(nèi)存不夠的時(shí)候會(huì)清除所有緩存圖片楷兽。
用搜索文件系統(tǒng)的方式做管理,文件替換方式是以時(shí)間為單位华临,剔除時(shí)間大于一周的圖片文件
當(dāng)SDWebImageManager向SDImageCache要資源時(shí)芯杀,先搜索內(nèi)存層面的數(shù)據(jù),如果有直接返回雅潭,沒有的話去訪問磁盤揭厚,將圖片從磁盤讀取出來,然后做Decoder寻馏,將圖片對(duì)象放到內(nèi)存層面做備份棋弥,再返回調(diào)用層。
- memory cache
@interface SDImageCache ()
#pragma mark - Properties
@property (strong, nonatomic, nonnull) NSCache *memCache;
// 這里我們發(fā)現(xiàn)诚欠, 有一個(gè)叫做 memCache 的屬性,它是一個(gè) NSCache 對(duì)象漾岳,用于實(shí)現(xiàn)我們對(duì)圖片的 Memory Cache轰绵。
// NSCache 簡(jiǎn)單來說,它是一個(gè)類似于 NSDictionary 的集合類尼荆,用于在內(nèi)存中存儲(chǔ)我們要緩存的數(shù)據(jù)左腔。詳細(xì)信息大家可以參考官方文檔:https://developer.apple.com/reference/foundation/nscache
/ 接受系統(tǒng)的內(nèi)存警告通知,然后清除掉自身的圖片緩存
@interface AutoPurgeCache : NSCache
@end
@implementation AutoPurgeCache
- (nonnull instancetype)init {
self = [super init];
if (self) {
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
}
return self;
}
2.Disk Cache:
也就是文件緩存捅儒,SDWebImage 會(huì)將圖片存放到 NSCachesDirectory 目錄中液样,然后為每一個(gè)緩存文件生成一個(gè) md5 文件名, 存放到文件中。
3.Disk 緩存清理策略
SDWebImage 會(huì)在每次 APP 結(jié)束和程序切到后臺(tái)的時(shí)候執(zhí)行清理任務(wù)巧还。 清理緩存的規(guī)則分兩步進(jìn)行鞭莽。 第一步先清除掉過期的緩存文件。 如果清除掉過期的緩存之后麸祷,空間還不夠澎怒。 那么就繼續(xù)按文件時(shí)間從早到晚排序,先清除最早的緩存文件阶牍,直到剩余空間達(dá)到要求喷面。
具體點(diǎn),SDWebImage 是怎么控制哪些緩存過期走孽,以及剩余空間多少才夠呢惧辈? 通過兩個(gè)屬性:
@interface SDImageCache :
NSObject@property (assign, nonatomic) NSInteger maxCacheAge;//文件緩存的時(shí)長(zhǎng)
@property (assign, nonatomic) NSUInteger maxCacheSize;//允許的最大緩存空間
// maxCacheAge 的默認(rèn)值
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
// maxCacheSize 的默認(rèn)值
[SDImageCache sharedImageCache].maxCacheSize = 1024 * 1024 * 50; // 50M
四、圖片Decoder
Q: 為什么要進(jìn)行圖片Decoder磕瓷?
A: png,jpeg等格式的數(shù)據(jù)是不能直接使用的,需要將其轉(zhuǎn)化為位圖(bitMap)
SDWebImage部份可以參考:+ (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image
函數(shù)
Q: imageView 加載一張圖片的過程 ?
A:
當(dāng)我們使用imageView顯示圖片的時(shí)候:
- 讀取圖片
- 解壓圖片為位圖(消耗CPU)
- 如果位圖數(shù)據(jù)不是字節(jié)對(duì)齊的盒齿,CoreAnimation會(huì)copy一份位圖數(shù)據(jù)并進(jìn)行字節(jié)對(duì)齊
- CoreAnimation渲染解壓縮過的位圖
- 這一切在IOS中都是默認(rèn)發(fā)生在主線程成的并且是在
UIImageView
執(zhí)行setImage
方法的時(shí)候完成的
總結(jié):
其實(shí)不解碼也是可以使用的,假如說我們通過imageNamed:來加載image,系統(tǒng)默認(rèn)會(huì)在主線程立即進(jìn)行圖片的解碼工作县昂。這一過程就是把image解碼成可供控件直接使用的位圖肮柜。
當(dāng)在主線程調(diào)用了大量的imageNamed:
方法后,就會(huì)產(chǎn)生卡頓了倒彰。為了解決這個(gè)問題我們有兩種比較簡(jiǎn)單的處理方法:
1.我們不使用imageNamed:
加載圖片审洞,使用其他的方法,比如imageWithContentsOfFile:
2.我們自己解碼圖片待讳,可以把這個(gè)解碼過程放到子線程
好處:
- 把圖片解碼這個(gè)默認(rèn)在主線程執(zhí)行,耗損CPU的行為,放在了后臺(tái)線程.
- 只需要在使用的時(shí)候,直接setImage,不會(huì)有太大的CPU消耗
弊端:
- 這樣解碼就是以空間換時(shí)間的方法,提前解壓好,用的時(shí)候直接從內(nèi)存讀取.
- 如果下載的圖片比較大,然后直接解碼的話 這個(gè)是內(nèi)存所不能承受,需要對(duì)圖片進(jìn)行壓縮.
補(bǔ)充些其他的
+(nullableUIImage*)imageNamed:(NSString*)name
:不適合加載大的 不常用的圖片.因?yàn)樗鼤?huì)默認(rèn)在程序內(nèi)存里保存這張圖片數(shù)據(jù)(不會(huì)隨ImageView的移除而移除).只有經(jīng)常使用圖片適合這種方式加載.+ (nullableUIImage*)imageWithContentsOfFile:(NSString*)path
:這個(gè)方法跟上面的略有不同,他不會(huì)在內(nèi)存中保留一份數(shù)據(jù).只要imageView移除,內(nèi)存中的數(shù)據(jù)就會(huì)直接移除.這也就是這個(gè)方法為什么適合加載大的圖片,但卻不常用的圖片.- 相關(guān)的參考文章:IOS異步圖片加載與常用的優(yōu)化