讀完這篇文章你可以自己寫一個(gè) 輕量級別的SDWebImage神器真朗,這篇文章類似源碼解析望蜡。但不同的是,不僅僅是解析颗味,會帶你手把手?jǐn)]一個(gè)精簡版的SDWebImage,更深刻的理解SDWebImage的架構(gòu)和一些核心類的功能勋桶。學(xué)習(xí)一個(gè)東西必須要總結(jié)一次才能更加理解其中的精華脱衙,否則即便是讀完了源碼也學(xué)不到多少核心的動心。好記性不如爛筆頭例驹。
注:SDWebImage有很多功能捐韩,我這里就實(shí)現(xiàn)了一個(gè)核心的功能,多圖異步下載鹃锈、內(nèi)存緩存荤胁、磁盤緩存。
輕量級仿SDWebImage源碼下載鏈接:https://github.com/ZhengYaWei1992/ZWWebImageCache
就直接從功能實(shí)現(xiàn)開始屎债,仿SDWebImage架構(gòu)手把手?jǐn)]一個(gè)輕量級SDWebImage仅政,之后在針對SDWebImage框架深入分析,因?yàn)榍捌诘膶?shí)現(xiàn)都是模仿SDWebImage的實(shí)現(xiàn)盆驹,所以當(dāng)你看懂前面的部分后圆丹,再去理解SDWebImage就是太輕而易舉的事了。
一躯喇、輕量級SDWebImage的實(shí)現(xiàn)
1.1 辫封、仿SDWebImage的架構(gòu)設(shè)計(jì)思路
先總的看一下架構(gòu)設(shè)計(jì)思路,如下圖:
這個(gè)圖可以先從Controller看起廉丽,Controller主要是否則調(diào)用UIImageView扥類中的方法倦微,相信大家都知道SDWebImage最基本的設(shè)置圖像的方法,需要導(dǎo)入U(xiǎn)IImageView+webCache這個(gè)分類正压,這個(gè)架構(gòu)同樣是采用這種方法欣福,通過UIImageView的分類設(shè)置圖像,只用簡單的傳入一個(gè)urlString即可焦履,當(dāng)然占位圖是必然支持的拓劝。
下載操作類:
UIImageView分類中包含下載操作管理類雏逾,下載操作管理類被設(shè)計(jì)為一個(gè)單例對象,它是這個(gè)架構(gòu)中的核心郑临,緩存校套、下載操作都是由它同意進(jìn)行管理。因?yàn)榫彺骖惐旧砩婕皟?nèi)容不是很多牧抵,所以這里就沒有給緩存類單獨(dú)抽離開來。
下載操作類:
首先要說明一下侨把,圖片的異步下載犀变,這里主要是通過NSOperationQueue這個(gè)類實(shí)現(xiàn)的。所謂的下載類就是NSOperation秋柄,每一個(gè)操作都對應(yīng)一個(gè)實(shí)例對象获枝。下載操作類的實(shí)現(xiàn),這里主要是自定義一個(gè)類繼承自NSOperation骇笔,自定義下載操作省店。 它同樣是由下載操作管理類進(jìn)行管理。
#######關(guān)于緩存:
SDWebImage的緩存形式實(shí)際上是包含內(nèi)存緩存和磁盤緩存笨触。其中內(nèi)存緩存主要是借助NSCache這個(gè)類實(shí)現(xiàn)的懦傍,磁盤緩存就是常規(guī)的文件讀取啦。同樣這個(gè)輕量級的SDWebImage內(nèi)存緩存也是借助NSCache這個(gè)類實(shí)現(xiàn)的芦劣。關(guān)于NSCache這個(gè)類粗俱,可能日常開發(fā)中用的不是很普遍,但使用起來還是很簡單的虚吟,基本使用形式和字典類似寸认,但是又有很多和字典不同之處。這篇文章中會說一些NSCache的使用和注意事項(xiàng)串慰。
1.2 下載操作類的實(shí)現(xiàn)
下載操作類實(shí)際是一個(gè)繼承與 NSOperation的自定義類偏塞,該類中主要有兩個(gè)核心方法:初始化對象和系統(tǒng)方法main。說明:main方法是系統(tǒng)方法邦鲫,在操作添加到隊(duì)列的時(shí)候會調(diào)用此方法灸叼。對外提供了兩個(gè)屬性圖片的urlString以及下載完成的回調(diào)(主要是在main方法中實(shí)現(xiàn))。
操作初始化方法
+ (instancetype)downloaderOperationWithURLString:(NSString *)urlString finishedBlock:(void (^)(UIImage *image))finishedBlock{
ZWDownloadOperation *op = [[ZWDownloadOperation alloc]init];
op.urlString = urlString;
op.finishedBlock = finishedBlock;
return op;
}
重寫系統(tǒng)main方法掂碱,當(dāng)外部將該類的實(shí)例對象添加到隊(duì)列中時(shí)怜姿,會調(diào)用mian方法。main方法中之所以會出現(xiàn)自動autoreleasepool疼燥,主要是因?yàn)楫惒讲僮鳠o法訪問主線程的自動釋放池沧卢,所以要手動自己添加釋放池。調(diào)用此方法會將讀取的圖片緩到磁盤中醉者,下載完成后但狭,會回到主線程產(chǎn)生回調(diào)披诗,并返回UIImage對象。
//重寫main方法 操作添加到隊(duì)列的時(shí)候會調(diào)用該方法
- (void)main{
//創(chuàng)建自動釋放池:因如果是異步操作立磁,無法訪問主線程的自動釋放池
@autoreleasepool {
//斷言
//添加斷言后呈队,if (self.finishedBlock) 不用再設(shè)置,如果為空了唱歧,程序會崩潰宪摧,同時(shí)會提醒:finishedBlock不能為空
NSAssert(self.finishedBlock != nil, @"finishedBlock不能為空");
//下載網(wǎng)絡(luò)圖片
NSURL *url = [NSURL URLWithString:self.urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
//緩存到沙盒中
if (data) {
[data writeToFile:[self.urlString appendCacheDir] atomically:YES];
}
//這里是子線程
//NSLog(@"下載圖片 %@ %@",self.urlString,[NSThread currentThread]);
NSLog(@"從網(wǎng)絡(luò)下載圖片");
//判斷?操作是否被取消
//如果取消,直接return颅崩。放在耗時(shí)操作之后和合理一些几于,取消操作的時(shí)候,不會攔截耗時(shí)操作沿后,耗時(shí)操作依然可以執(zhí)行沿彭。下次想顯示圖像的時(shí)候,耗時(shí)操作也執(zhí)行完畢
if (self.isCancelled) {
return;
}
//圖片下載完成回到主線程更新UI 通過使用斷言尖滚,這里就不用使用if (self.finishedBlock) 判斷了
//if (self.finishedBlock) {
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
UIImage *img = [UIImage imageWithData:data];
self.finishedBlock(img);
}];
//}
}
}
1.3 下載操作管理類
毫無疑問喉刘,這個(gè)類必然是單例對象,管理類嗎漆弄,當(dāng)然要全局管理睦裳,有足夠高的權(quán)限才能夠被稱為管理者。這個(gè)類主要是對完提供了三個(gè)方法:1撼唾、創(chuàng)建單例對象 2推沸、開啟下載任務(wù) 3、取消操作券坞,因?yàn)橐紤]到重復(fù)下載的情況鬓催,所以要對外提供這樣一個(gè)接口。
說是下載操作管理類恨锚,實(shí)際上是有點(diǎn)不合適的宇驾,應(yīng)為該類主要用有兩個(gè)功能:管理全局下載和管理全局緩存。全局緩存沒有單獨(dú)抽離出來猴伶,暫時(shí)就稱為下載操作管理類就行课舍,緩存會單獨(dú)講解一些的。實(shí)際SDWebImage的緩存功能室單獨(dú)抽取出來的他挎。
下載操作管理類主要提供了這樣三個(gè)屬性筝尾。分別是全局隊(duì)列、下載操作緩存池办桨、圖片內(nèi)存緩存池筹淫。之所以會有下載操作緩存池,是因?yàn)橐涗浵螺d操作呢撞,如果下載操作已經(jīng)存在就不用再去執(zhí)行下載方法损姜,直接reture饰剥,避免重復(fù)下載這種情況的出現(xiàn)。開始下載圖片的時(shí)候摧阅,將草案做添加到操作緩存翅中汰蓉。圖片下載完成后,操作要從操作緩存池中移除棒卷。
//全局隊(duì)列
@property(nonatomic,strong)NSOperationQueue *queue;
//下載操作緩存池 這里不能改為NSCache顾孽,因?yàn)槭盏絻?nèi)存警告后NSCache移除所有對象,之后NSCache中就無法繼續(xù)添加數(shù)據(jù)了
@property(nonatomic,strong)NSMutableDictionary *operationCache;
//圖片緩存池(內(nèi)存緩存) 從字典改為NSCache
@property(nonatomic,strong)NSCache *imageCache;
下載方法的實(shí)現(xiàn)比规。
總的思路是這樣的岩齿,先判斷下載操作是否存在,如存在直接返回苞俘,避免重復(fù)下載。之后根據(jù)圖片地址urlString判斷是否存在內(nèi)存緩存和磁盤緩存龄章,如果存在直接調(diào)用回調(diào)吃谣,如過不存在就創(chuàng)建操作對象,添加到全局隊(duì)列做裙,開啟下載任務(wù)岗憋。
- (void)downloadWithURLString:(NSString *)urlString finishedBlock:(void (^)(UIImage *image))finishedBlock{
//斷言
NSAssert(finishedBlock != nil, @"finishedBlock不能為空");
//如果下載操作已經(jīng)存在,直接返回锚贱。避免重復(fù)下載
if (self.operationCache[urlString]) {
return;
}
//判斷圖片是否有緩存(內(nèi)存和磁盤緩存)
if ([self checkImageCache:urlString]) {
//如果有緩存仔戈,就要回調(diào)設(shè)置圖像
finishedBlock([self.imageCache objectForKey:urlString]);
return;
}
ZWDownloadOperation *op = [ZWDownloadOperation downloaderOperationWithURLString:urlString finishedBlock:^(UIImage *image) {
//回調(diào)
finishedBlock(image);
//緩存圖片(內(nèi)存緩存)
//self.imageCache[urlString] = image;
[self.imageCache setObject:image forKey:urlString];
//下載完成,移除緩存的操作
[self.operationCache removeObjectForKey:urlString];
}];
[self.queue addOperation:op];
//緩存下載操作
self.operationCache[urlString] = op;
}
關(guān)于取消操作拧廊。
取消操作中藥判斷urlString是否為空监徘,如果不做此判斷,當(dāng)urlString為nil的時(shí)候吧碾,執(zhí)行 [self.operationCache removeObjectForKey:urlString];會發(fā)生崩潰凰盔。
//取消操作
- (void)cancelOperation:(NSString *)urlString{
//避免第一次urlString為空,然后調(diào)用[self.operationCache removeObjectForKey:urlString]導(dǎo)致崩潰的問題
if (urlString == nil) {
return;
}
[self.operationCache[urlString] cancel];
//從緩存池移除操作
[self.operationCache removeObjectForKey:urlString];
}
關(guān)于緩存倦春。
對于緩存要明確明白分為內(nèi)存緩存和磁盤還盤户敬,在調(diào)用該類執(zhí)行下載操作的時(shí)候,要首先判斷是否有緩存睁本。有緩存就回調(diào)緩存圖片尿庐,無緩存就執(zhí)行下載。但是內(nèi)存緩存和磁盤緩存也是有一些注意的地方呢堰,判斷是否有緩存應(yīng)該判斷是否有內(nèi)存緩存抄瑟,如果有直接回調(diào);如果沒再去判斷是否有磁盤緩存枉疼。如果有磁盤緩存锐借,直接回調(diào)问麸,并將磁盤緩存圖像添加到圖像緩存中,下次再去判斷這張圖片的時(shí)候就可以從內(nèi)存緩存池中讀取钞翔。如果磁盤沒有緩存严卖,最后在開啟下載圖片任務(wù)。
//檢查是否有緩存(內(nèi)存緩存和磁盤緩存)
- (BOOL) checkImageCache:(NSString *)urlString{
//1布轿、檢查內(nèi)存緩存
if ([self.imageCache objectForKey:urlString]) {
NSLog(@"從內(nèi)存中加載");
return YES;
}
//2哮笆、檢查沙盒緩存
UIImage *img = [UIImage imageWithContentsOfFile:[urlString appendCacheDir]];
//NSLog(@"沙盒路徑:%@",[urlString appendCacheDir]);
if (img) {
NSLog(@"從沙盒中加載 ");
//如果沙盒有圖片,要保存到內(nèi)存中============
//self.imageCache[urlString] = img;
[self.imageCache setObject:img forKey:urlString];
return YES;
}
return NO;
}
關(guān)于NSCache
NSCache使用起來基本和字典雷士汰扭,但是有一些注意點(diǎn)稠肘,同事功能比字典強(qiáng)大,因?yàn)榭梢栽O(shè)置緩存限額萝毛,當(dāng)超過限額的時(shí)候项阴,會自動移除之前的記錄,然后添加新的記錄笆包』防浚基本使用就是四句代碼。但是對于移除所有數(shù)據(jù)有一點(diǎn)值得注意的庵佣,通常在使用NSCache的時(shí)候可以在didReceiveMemoryWarning收到內(nèi)存警告方法中調(diào)用[self.cache removeAllObjects];這句代碼歉胶。調(diào)用removeAllObjects之后,就無法再次往cache中緩存數(shù)據(jù)巴粪。但是如果不是在收到內(nèi)存警告中removeAllObjects通今,依然是可以正常添加數(shù)據(jù)的。實(shí)際開發(fā)中應(yīng)重視到這一點(diǎn)肛根。
//設(shè)置數(shù)據(jù)限額
_cache.countLimit = 5;
//添加或替換數(shù)據(jù)
[self.cache setObject:@"sss" forKey:@"a"];
//根據(jù)key獲取數(shù)據(jù)
[self.cache objectForKey:@"a"];
//移除所有數(shù)據(jù)
[self.cache removeAllObjects];
1.4 關(guān)于UIImageView的分類實(shí)現(xiàn)
分類中主要有一個(gè)核心方法辫塌,設(shè)置UIImageView的圖片。直接一行代碼調(diào)用派哲。這里同樣考慮到一點(diǎn)就是頻繁改變UIImageView的圖片璃氢。假設(shè)在控制器的touchBegan方法中每次點(diǎn)擊都會改變imageView的圖片,點(diǎn)擊多少次圖片就會連續(xù)切換多少次狮辽。但是加入不想讓圖片連續(xù)切換一也,只要顯示第一張圖片和最后一張圖片即可,其他中間觸發(fā)時(shí)間的圖片就不要顯示了喉脖,并且取消下載任務(wù)椰苟。為了滿足這個(gè)需要,所以要借助運(yùn)行時(shí)的關(guān)聯(lián)對象增加屬性树叽,記錄當(dāng)前圖片的urlString地址舆蝴。除了這些額外的處理外,核心就是調(diào)用操作管理類中的獲取圖片的方法。實(shí)現(xiàn)代碼如下洁仗。
- (void)zw_setImageWithUrlString:(NSString *)urlString{
//防止連續(xù)設(shè)置圖片层皱,UIImageView上的圖片頻繁切換
//判斷當(dāng)前點(diǎn)擊的圖片地址和上一次圖片的地址是否一樣,如果不一樣取消上一次操作
if (![urlString isEqualToString:self.currentURLString]) {
//取消上一次操作
//[self.operationCache[self.currentURLString] cancel];
[[ZWDownloderOperationManager sharedManager]cancelOperation:self.currentURLString];
}
//記錄上一次的圖片地址
self.currentURLString = urlString;
//下載圖片
[[ZWDownloderOperationManager sharedManager]downloadWithURLString:urlString finishedBlock:^(UIImage *image) {
self.image = image;
}];
}
關(guān)聯(lián)對象擴(kuò)充屬性赠潦。
- (void)setCurrentURLString:(NSString *)currentURLString{
objc_setAssociatedObject(self, @"currentURLString", currentURLString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)currentURLString{
return objc_getAssociatedObject(self, @"currentURLString");
}
當(dāng)然還可以在此基礎(chǔ)上擴(kuò)展一個(gè)設(shè)置占位圖的方法叫胖,代碼如下。
- (void)zw_setImageWithUrlString:(NSString *)urlString withPlaceHolderImageName:(NSString *)placeholderStr{
self.image = [UIImage imageNamed:placeholderStr];
[self zw_setImageWithUrlString:urlString];
}
見證成果的時(shí)刻
好了她奥,基本就這些代碼瓮增,剩下的直接在控制器中調(diào)用UIImageView分類中的方法即可,直接上效果圖啦哩俭。
二绷跑、SDWebImage框架結(jié)構(gòu)
2.1 SDWebImage中有四個(gè)核心的類,以及一些分類凡资。
四大核心類以及其關(guān)系:
SDWebImageDownloader
砸捏、SDWebImageDownloaderOperation
、SDWebImageManager
隙赁、SDImageCache
核心分類:
UIView+WebCacheOperation
:主要在這個(gè)類中處理操作
UIButton+WebCache
:button上圖片緩存
UIImage+GIF
: gif圖片顯示
UIImageView+WebCach
e:imageView上的圖片緩存
NSData+ImageContentType
:獲取文件類型
類的包含關(guān)系:
UIImageView+WebCache
中包含SDWebImageManager
和UIView+WebCacheOperation
垦藏,
SDWebImageManager
包含SDWebImageDownloader
SDWebImageManager
包含SDImageCache
SDWebImageDownloader
包含SDWebImageDownloaderOperation
。
其中SDWebImageDownloader
是負(fù)責(zé)下載的類鸳谜。SDWebImageDownloaderOperation
是下載操作類,繼承于NSOperation式廷。SDWebImageDownloader
中包含SDWebImageDownloaderOperation
的頭文件咐扭,前者依賴于后者。
SDImageCache
主要用于緩存處理滑废,緩存處理同樣是分為內(nèi)存緩存和磁盤緩存蝗肪,并定義了 SDImageCacheType
枚舉用于區(qū)分緩存類型。
SDWebImageManager
中主要包含了SDWebImageDownloader
和SDImageCache
蠕趁,并且還有一個(gè)創(chuàng)建單例的方法薛闪。這個(gè)類是一個(gè)核心的管理類,將緩存和下載的業(yè)務(wù)邏輯統(tǒng)一在一起俺陋,和我們實(shí)現(xiàn)的輕量級的圖片緩存不同的是豁延,我們將緩存的業(yè)務(wù)邏輯直接放置到管理類中,并沒有單獨(dú)抽取出來腊状。
UIImageView+WebCache
分類中包含SDWebImageManager
和UIView+WebCacheOperation
核心類诱咏。UIImageView+WebCache
中有一個(gè)sd_cancelCurrentImageLoad
方法,這個(gè)方法主要是在取消當(dāng)前圖片下載缴挖,防止重復(fù)下載操作袋狞。具體實(shí)現(xiàn)放在UIView+WebCacheOperation
中
整的來說,和我們之前的實(shí)現(xiàn)還是很類似的。實(shí)際上我們實(shí)現(xiàn)的是模仿SDWebImage
實(shí)現(xiàn)的一個(gè)簡單的圖片緩存處理苟鸯。??
另外同蜻,SDWebImageDownloader
在初始化initialize
的時(shí)候添加了一些通知,主要用于監(jiān)聽下載任務(wù)早处,顯示加載指示器湾蔓。
2.2 SDWebImage的緩存
SDWebImage的緩存也是分為內(nèi)存緩存和磁盤緩存:其中內(nèi)存緩存同樣主要是通過NSCache處理。
磁盤緩存處理中會設(shè)置自動清理磁盤空間陕赃,清理周期設(shè)置為一周卵蛉。SDWebImageCache中有這樣一個(gè)屬性,@property (assign, nonatomic) NSInteger maxCacheAge;
該屬性默認(rèn)值是一周(kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7
)設(shè)置清理磁盤緩存的周期么库。
處理方式傻丝,請看SDWebCache
中的- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock
方法。這個(gè)方法中首先看到的是異步操作诉儒,因?yàn)樘幚砦募^多時(shí)葡缰,比較消耗資源最好是異步的方式處理。下面的代碼是定時(shí)清理磁盤緩存方法中的部分代碼忱反,思路是獲取到一周前的時(shí)間泛释,再通過NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]
獲取文件的時(shí)間,比較兩個(gè)是時(shí)間温算,如果大于一周的時(shí)間怜校,就將文件路徑添加到urlsToDelete待刪除數(shù)組中,最后遍歷這個(gè)數(shù)組注竿,統(tǒng)一刪除過期資源茄茁。
//獲取一周前的時(shí)間
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
//用于記錄當(dāng)前文件總大小
NSUInteger currentCacheSize = 0;
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
//獲取到多余一周前的時(shí)間,并添加到帶刪除的數(shù)組中
[urlsToDelete addObject:fileURL];
continue;
}
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
}
//刪除超過一周時(shí)間的文件
for (NSURL *fileURL in urlsToDelete) {
[_fileManager removeItemAtURL:fileURL error:nil];
}
2.3 關(guān)于SDWebImage的幾個(gè)小問題:
1巩割、最大并發(fā)數(shù)多少裙顽?
SDWebImageDownloader.m文件中的init方法中有這樣一行代碼。即最大線程并發(fā)數(shù)為6宣谈,實(shí)際開發(fā)中并不是開啟的線程越多越好愈犹,當(dāng)線程過多的時(shí)候也會影響性能,一般建議線程不要超過8前后闻丑。
_downloadQueue.maxConcurrentOperationCount = 6;
2漩怎、是否支持gif?
支持gif嗦嗡,主要是在UIImage+gif這個(gè)分類中扬卷。這個(gè)類中總共就只有三個(gè)方法。
self.imageView.image = [UIImage sd_animatedGIFNamed:@"1.gif"];
按照如上方式設(shè)置的gif圖片酸钦,在一些情況可能無法正常顯示gif圖片怪得,這個(gè)是新版本SDWebImage的bug咱枉,老版本中不存在這樣的問題。設(shè)置gif圖片最好實(shí)時(shí)通過下面這個(gè)方法徒恋。
self.imageView.image = [UIImage sd_animatedGIFNamed:@"1.gif"];
NSString *filePath = [[NSBundle bundleWithPath:[[NSBundle mainBundle] bundlePath]] pathForResource:@"1.gif" ofType:nil];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
self.imageView.image = [UIImage sd_animatedGIFWithData:imageData];
3蚕断、如何判斷文件的類型?
NSData+ImageContentType.m中入挣,sd_contentTypeForImageData方法可以獲取到文件的類型亿乳。其中 [data getBytes:&c length:1];是獲取文件的第一個(gè)字節(jié),文件的第一個(gè)字節(jié)中包含文件類型相關(guān)的信息径筏。
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52:
// R as RIFF for WEBP
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
return nil;
}
4葛假、磁盤緩存文件名稱是什么?
通過命名空間com.hackemist.SDWebImageCache.隔離區(qū)分滋恬。
為了防止圖片名沖突聊训,根據(jù)MD5計(jì)算。md5的重復(fù)幾率是很小的恢氯。