【轉載】基於AFNetWorking3.0的圖片緩存分析

原文出處:Yasin的簡書 http://www.jianshu.com/p/b1045c3fc8d0git

 

圖片在APP中佔有重要的角色,對圖片作好緩存是重要的一項工做。
[TOC]github

理論

不喜歡理論的能夠直接跳到下面的Demo實踐部分算法

緩存介紹

緩存按照保存位置能夠分爲兩類:內存緩存、硬盤緩存(FMDB、CoreData...)。
咱們常說的數據緩存包含內存緩存、硬盤緩存和網絡請求URL緩存。其中網絡請求URL緩存也包含內存緩存和硬盤緩存。數據庫

圖片緩存思路



URL緩存(緩存請求)

網絡請求除了客戶端須要作簡單的配置外,最主要須要服務器支持,服務端也很簡單,只須要在response裏面設置Cache-Control字段就好了.緩存

最多見的URL緩存實現方式:NSURLCache。NSURLCache能夠在memory 和 disk 上緩存。
AFNetWorking是基於NSURLSession(iOS7以上的網絡請求框架),在生成配置的時候有三種配置選擇服務器

1 + (NSURLSessionConfiguration *)defaultSessionConfiguration;  
2 //默認會話模式(default):工做模式相似於原來的NSURLConnection,使用的是基於磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證受權。
3 + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;  
4 //瞬時會話模式(ephemeral):該模式不使用磁盤保存任何數據。全部和會話相關的caches,證書,cookies等都被保存在RAM中,所以當程序使會話無效,這些緩存的數據就會被自動清空。
5 + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;  
6 //後臺會話模式(background):該模式在後臺完成上傳和下載,在建立Configuration對象的時候須要提供一個NSString類型的ID用於標識完成工做的後臺會話。

也就是說default同時實現了內存緩存和硬盤緩存,ephemeral實現了內存緩存,對於圖片下載咱們固然選擇default。
咱們還能夠對緩存的大小進行設置,只須要對NSURLCache進行初始化就能夠了cookie

實現初始化

-application:didFinishLaunchingWithOptions:中對[NSURLCache sharedURLCache]進行初始化設置:網絡

1 NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
2                                                          diskCapacity:20 * 1024 * 1024
3                                                              diskPath:nil];
4 [NSURLCache setSharedURLCache:URLCache];

也能夠單獨對NSURLSessionconfiguration進行設置,
在AFNetWorking中對於圖片網絡請求設置了20M的內存緩存和150M的硬盤緩存:session

1 + (NSURLCache *)defaultURLCache {
2     return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
3                                          diskCapacity:150 * 1024 * 1024
4                                              diskPath:@"com.alamofire.imagedownloader"];
5 }

緩存策略

緩存策略是指對網絡請求緩存若是處理,是使用緩存仍是不使用app

 1 NSURLRequestUseProtocolCachePolicy: 對特定的 URL 請求使用網絡協議中實現的緩存邏輯。這是默認的策略。
 2 NSURLRequestReloadIgnoringLocalCacheData:數據須要從原始地址加載。不使用現有緩存。
 3 NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不只忽略本地緩存,
 4       同時也忽略代理服務器或其餘中間介質目前已有的、協議容許的緩存。
 5 NSURLRequestReturnCacheDataElseLoad:不管緩存是否過時,先使用本地緩存數據。
 6       若是緩存中沒有請求所對應的數據,那麼從原始地址加載數據。
 7 NSURLRequestReturnCacheDataDontLoad:不管緩存是否過時,先使用本地緩存數據。
 8       若是緩存中沒有請求所對應的數據,那麼放棄從原始地址加載數據,
 9       請求視爲失敗(即:「離線」模式)。
10 NSURLRequestReloadRevalidatingCacheData:從原始地址確認緩存數據的合法性後,
11       緩存數據就可使用,不然從原始地址加載。

在AFNetWorking中一樣對configuration進行設置

1 configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

若是你繼承AFImageDownloader從新實現了他的初始化,requestCachePolicy注意AFImageDownloader中只有三種才設置了緩存

1 case NSURLRequestUseProtocolCachePolicy:
2 case NSURLRequestReturnCacheDataElseLoad:
3 case NSURLRequestReturnCacheDataDontLoad:

圖片內存緩存

AFNetWorking3.0放棄了NSCache做爲圖片內存緩存管理,這讓我很是不解。
有人說它的性能和 key 的類似度有關,若是有大量類似的 key (好比 "1", "2", "3", ...),NSCache 的存取性能會降低得很是厲害,大量的時間被消耗在 CFStringEqual() 上,不知這是否是放棄使用NSCache的緣由。

像素在內存中的佈局和它在磁盤中的存儲方式並不相同。考慮一種簡單的狀況:每一個像素有R、G、B和alpha四個值,每一個值佔用1字節,所以每一個像素佔用4字節的內存空間。一張1920*1080的照片(iPhone6 Plus的分辨率)一共有2,073,600個像素,所以佔用了超過8Mb的內存。可是一張一樣分辨率的PNG格式或JPEG格式的圖片通常狀況下不會有這麼大。這是由於JPEG將像素數據進行了一種很是複雜且可逆的轉化。

AFNetWorking3.0的圖片緩存類貌似是基於這個理論來作內存大小管理的(以前AF的內存大小計算方法有錯,我修改了一下提交了,如今已經審覈經過合併進去了,哈哈哈哈哈,我也算是貢獻過AF了)。AFNetWorking2.x中仍是使用AFImageCache進行memory上緩存。

NSCache在memory上緩存,相似於NSMutableDictionary ,以 哈希算法 管理。有自動清理機制,當緩存到memory時,若是memory空間不夠,則會自動刪除memory中當前界面不使用的空間。

AFAutoPurgingImageCache使用NSMutableDictionary <NSString* , AFCachedImage*>進行內存緩存映射,並進行管理,當內存警告時就清空NSMutableDictionary。若是內存佔用超過限制,則按照時間順序進行刪除。

圖片硬盤緩存

就是咱們常說的把數據保存在本地,好比FMDB、CoreData、歸檔、NSUserDefaults、NSFileManager等等,這裏就很少說了。
AFNetWorking3.0沒有直接作圖片硬盤緩存,而是經過URL緩存作的硬盤緩存。也就是說,若是內存緩存沒有讀取到圖片,就會調用下載邏輯,經過下載緩存的內存緩存硬盤緩存來獲取到已下載過的圖片,若是沒有下載過,就會從新下載。
若是咱們本身作圖片硬盤緩存建議使用NSFileManager,由於通常圖片data會比較大,測試證實路徑緩存會比放在數據庫有更高的性能。


實踐

 

使用NSURLSession作網絡請求緩存。

 1 NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];     //使用default配置,自帶網絡請求緩存
 2     [config setHTTPAdditionalHeaders:@{@"Accept":@"image/*"}];//設置網絡數據格式
 3     NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
 4     NSURLRequest *request = [NSURLRequest requestWithURL:url];
 5     WEAKSELF
 6     NSURLSessionDataTask *task = [session dataTaskWithRequest:request 
 7       completionHandler:^(NSData * _Nullable data, 
 8     NSURLResponse * _Nullable response, NSError * _Nullable error) { 
 9     //使用’獲取數據(NSURLSessionDataTask)‘的方式發起請求
10         UIImage *image = [UIImage imageWithData:data];
11         dispatch_async(dispatch_get_main_queue(), ^{
12             weakSelf.imageView.image = image;
13         });
14     }];
15     [task resume];

咱們經過連續兩次下載圖片能夠發現defaultSessionConfiguration下NSURLSession會自動作網絡請求緩存。

使用AFNetWorking下載圖片

導入頭文件#import "UIImageView+AFNetworking.h"
使用特別簡單,只有一行代碼:[imageView setImageWithURL:url];
UIImageView+AFNetworking作了內存緩存和基於NSURLSession的網絡請求緩存,並無作硬盤緩存,估計是考慮到圖片的網絡請求硬盤緩存足以知足須要,因此省略了額外的硬盤緩存,內存緩存加快了讀取速度,這個是很是有必要的。

進入UIImageView+AFNetworking代碼分析:

 

1 if ([urlRequest URL] == nil) {
2         [self cancelImageDownloadTask];
3         self.image = placeholderImage;
4         return;
5     }
6 //若是新傳入的URL爲空則取消圖片下載並設置圖片爲默認圖
1 if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
2         return;
3     }
4 //若是新傳入的URL與當前URL相同則直接返回,不然取消當前下載,從新進行圖片查找下載
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
//從內存緩存中讀取image,若是沒有則發起新的請求
1 AFImageDownloader *downloader = [[self class] sharedImageDownloader];
2 //使用單例下載,內存緩存爲downloader.imageCache
3 //downloader設置的網絡請求20M的內存緩存和150M的硬盤緩存
4 //downloader設置的網絡請求緩存策略爲NSURLRequestUseProtocolCachePolicy
5 //imageCache設置了內存60M最大100M
6 //網絡請求發起前會再次判斷imageCache中是否含有該image

測試

使用Charles查看圖片下載的網絡請求發生了幾回,判斷緩存是否成功。
其中硬盤緩存須要寫入時間,網絡請求完成後略等一下,不然硬盤緩存不會生效

設置默認網絡緩存大小

若是沒有對NSURLRequestURLCache進行設置,默認是使用[NSURLCache sharedURLCache],因此若是有須要能夠以下設置

 1 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 2     // Override point for customization after application launch.
 3     [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
 4     //網絡請求時狀態欄網絡狀態小轉輪
 5 
 6     NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
 7                                                          diskCapacity:20 * 1024 * 1024
 8                                                              diskPath:nil];
 9     //內存4M,硬盤20M
10     [NSURLCache setSharedURLCache:URLCache];
11 
12     return YES;
13 }
 
文/Yasin的簡書(簡書做者) 原文連接:http://www.jianshu.com/p/b1045c3fc8d0 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。
相關文章
相關標籤/搜索