基于AFNetWorking3.0的圖片緩存分析

emiliadicksonqw 8年前發布 | 26K 次閱讀 iOS開發 移動開發 AFNetworking

來自: http://ios.jobbole.com/84084/

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

理論

不喜歡理論的可以直接跳到下面的Demo實踐部分

緩存介紹

緩存按照保存位置可以分為兩類:內存緩存、硬盤緩存(FMDB、CoreData…)。

我們常說的 網絡請求緩存 包含內存緩存、硬盤緩存和URL緩存。

圖片緩存思路

圖片緩存流程圖.png

URL緩存

網絡請求除了客戶端需要做簡單的配置外,最主要需要服務器支持,服務端也很簡單,只需要在response里面設置Cache-Control字段就行了.

最常見的URL緩存實現方式: NSURLCache 。NSURLCache可以在memory 和 disk 上緩存。

AFNetWorking是基于 NSURLSession (iOS7以上的網絡請求框架),在生成配置的時候有三種配置選擇

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

也就是說default同時實現了內存緩存和硬盤緩存,ephemeral實現了內存緩存,對于圖片下載我們當然選擇default。我們還可以對緩存的大小進行設置,只需要對NSURLCache進行初始化就可以了

實現初始化

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

NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                         diskCapacity:20 * 1024 * 1024
                                                             diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
NSURLCache *URLCache = [[NSURLCachealloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                        diskCapacity:20 * 1024 * 1024
                                                            diskPath:nil];
[NSURLCachesetSharedURLCache:URLCache];

也可以單獨對 NSURLSession 的 configuration 進行設置,

在AFNetWorking中對于圖片網絡請求設置了20M的內存緩存和150M的硬盤緩存:

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

緩存策略

緩存策略是指對網絡請求緩存如果處理,是使用緩存還是不使用

NSURLRequestUseProtocolCachePolicy: 對特定的 URL 請求使用網絡協議中實現的緩存邏輯。這是默認的策略。
NSURLRequestReloadIgnoringLocalCacheData:數據需要從原始地址加載。不使用現有緩存。
NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不僅忽略本地緩存,
      同時也忽略代理服務器或其他中間介質目前已有的、協議允許的緩存。
NSURLRequestReturnCacheDataElseLoad:無論緩存是否過期,先使用本地緩存數據。
      如果緩存中沒有請求所對應的數據,那么從原始地址加載數據。
NSURLRequestReturnCacheDataDontLoad:無論緩存是否過期,先使用本地緩存數據。
      如果緩存中沒有請求所對應的數據,那么放棄從原始地址加載數據,
      請求視為失敗(即:“離線”模式)。
NSURLRequestReloadRevalidatingCacheData:從原始地址確認緩存數據的合法性后,
      緩存數據就可以使用,否則從原始地址加載。
NSURLRequestUseProtocolCachePolicy: 對特定的 URL 請求使用網絡協議中實現的緩存邏輯。這是默認的策略。
NSURLRequestReloadIgnoringLocalCacheData:數據需要從原始地址加載。不使用現有緩存。
NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不僅忽略本地緩存,
      同時也忽略代理服務器或其他中間介質目前已有的、協議允許的緩存。
NSURLRequestReturnCacheDataElseLoad:無論緩存是否過期,先使用本地緩存數據。
      如果緩存中沒有請求所對應的數據,那么從原始地址加載數據。
NSURLRequestReturnCacheDataDontLoad:無論緩存是否過期,先使用本地緩存數據。
      如果緩存中沒有請求所對應的數據,那么放棄從原始地址加載數據,
      請求視為失敗(即:“離線”模式)。
NSURLRequestReloadRevalidatingCacheData:從原始地址確認緩存數據的合法性后,
      緩存數據就可以使用,否則從原始地址加載。

在AFNetWorking中同樣對 configuration 進行設置

configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

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

case NSURLRequestUseProtocolCachePolicy:
case NSURLRequestReturnCacheDataElseLoad:
case NSURLRequestReturnCacheDataDontLoad:
case NSURLRequestUseProtocolCachePolicy:
case NSURLRequestReturnCacheDataElseLoad:
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 進行內存緩存映射,并進行管理,當內存警告時就清空NSMutableDictionary。如果內存占用超過限制,則按照時間順序進行刪除。

硬盤緩存

就是我們常說的把數據保存在本地,比如FMDB,CoreData,歸檔,NSUserDefaults,NSFileManager等等,這里就不多說了。圖片緩存建議使用NSFileManager,因為一般圖片data會比較大,測試證明路徑緩存會比放在數據庫有更高的性能。

實踐

Demo下載

使用NSURLSession做網絡請求緩存。

NSURLSessionConfiguration config = [NSURLSessionConfiguration defaultSessionConfiguration]; //使用default配置,自帶網絡請求緩存 [config setHTTPAdditionalHeaders:@{@"Accept":@"image/ "}];//設置網絡數據格式 NSURLSession session = [NSURLSession sessionWithConfiguration:config]; NSURLRequest request = [NSURLRequest requestWithURL:url]; WEAKSELF NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * Nullable data, NSURLResponse * Nullable response, NSError * Nullable error) { //使用’獲取數據(NSURLSessionDataTask)‘的方式發起請求 UIImage *image = [UIImage imageWithData:data]; dispatch async(dispatch get main_queue(), ^{ weakSelf.imageView.image = image; }); }]; [task resume];

    NSURLSessionConfiguration *config = [NSURLSessionConfigurationdefaultSessionConfiguration];    //使用default配置,自帶網絡請求緩存
    [configsetHTTPAdditionalHeaders:@{@"Accept":@"image/*"}];//設置網絡數據格式
    NSURLSession *session = [NSURLSessionsessionWithConfiguration:config];
    NSURLRequest *request = [NSURLRequestrequestWithURL:url];
    WEAKSELF
    NSURLSessionDataTask *task = [sessiondataTaskWithRequest:requestcompletionHandler:^(NSData * _Nullabledata, NSURLResponse * _Nullableresponse, NSError * _Nullableerror) { //使用’獲取數據(NSURLSessionDataTask)‘的方式發起請求
        UIImage *image = [UIImageimageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            weakSelf.imageView.image = image;
        });
    }];
    [taskresume];

使用AFNetWorking下載圖片

導入頭文件 #import "UIImageView+AFNetworking.h"
使用: [imageView setImageWithURL:url];
UIImageView+AFNetworking做了內存緩存,和基于NSURLSession的網絡請求緩存

代碼分析:

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

測試

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

設置默認網絡緩存大小

如果沒有對 NSURLRequest 的 URLCache 進行設置,默認是使用 [NSURLCache sharedURLCache] ,所以如果有需要可以如下設置

  • (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions { // Override point for customization after application launch. [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; //網絡請求時狀態欄網絡狀態小轉輪

    NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil]; //內存4M,硬盤20M [NSURLCache setSharedURLCache:URLCache];

    return YES; }

- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [AFNetworkActivityIndicatorManagersharedManager].enabled = YES;
    //網絡請求時狀態欄網絡狀態小轉輪
 
    NSURLCache *URLCache = [[NSURLCachealloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                        diskCapacity:20 * 1024 * 1024
                                                            diskPath:nil];
    //內存4M,硬盤20M
    [NSURLCachesetSharedURLCache:URLCache];
 
    return YES;
}
 本文由用戶 emiliadicksonqw 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!