你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> AFNetWorking是如何進行數據緩存的

AFNetWorking是如何進行數據緩存的

編輯:IOS開發基礎

原文-Tim Brandt《How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained》

如果你是一個正在使用由Matt Thompson開發的網絡庫 AFNetWorking(如果你還沒有使用,那你還在等什麼?)的iOS開發者,也許你一直很好奇和困惑它的緩存機制,並且想要了解如何更好地充分利用它?

AFNetworking實際上利用了兩套單獨的緩存機制:

  • AFImagecache : 繼承於NSCache,AFNetworking的圖片內存緩存的類。

  • NSURLCache : NSURLConnection的默認緩存機制,用於存儲NSURLResponse對象:一個默認緩存在內存,並且可以通過一些配置操作可以持久緩存到磁盤的類。

AFImageCache是如何工作的?

AFImageCache屬於UIImageView+AFNetworking的一部分,繼承於NSCache,以URL(從NSURLRequest對象中獲取)字符串作為key值來存儲UIImage對象。 AFImageCache的定義如下:(這裡我們聲明了一個2M內存、100M磁盤空間的NSURLCache對象。)

@interface AFImageCache : NSCache // singleton instantiation :

+ (id )sharedImageCache {
    static AFImageCache *_af_defaultImageCache = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _af_defaultImageCache = [[AFImageCache alloc] init];

// clears out cache on memory warning :

    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) {
        [_af_defaultImageCache removeAllObjects];
    }];
});

// key from [[NSURLRequest URL] absoluteString] :

static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) {
    return [[request URL] absoluteString];
}

@implementation AFImageCache

// write to cache if proper policy on NSURLRequest :

- (UIImage *)cachedImageForRequest:(NSURLRequest *)request {
    switch ([request cachePolicy]) {
        case NSURLRequestReloadIgnoringCacheData:
        case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
            return nil;
        default:
            break;
    }

    return [self objectForKey:AFImageCacheKeyFromURLRequest(request)];
}
// read from cache :

- (void)cacheImage:(UIImage *)image
        forRequest:(NSURLRequest *)request {
    if (image && request) {
        [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)];
    }
}

AFImageCache是NSCache的私有實現,它把所有可訪問的UIImage對象存入NSCache中,並控制著UIImage對象應該在何時釋放,如果UIImage對象釋放的時候你希望去做一些監聽操作,你可以實現NSCacheDelegate的 cache:willEvictObject 代理方法。Matt Thompson已經謙虛的告訴我在AFNetworking2.1版本中可通過setSharedImageCache方法來配置AFImageCache,這裡是 AFN2.2.1中的UIImageView+AFNetworking文檔。

NSURLCache

AFNetworking使用了NSURLConnection,它利用了iOS原生的緩存機制,並且NSURLCache緩存了服務器返回的NSURLRespone對象。NSURLCache 的shareCache方法是默認開啟的,你可以利用它來獲取每一個NSURLConnection對象的URL內容。讓人不爽的是,它的默認配置是緩存到內存而且並沒有寫入到磁盤。為了tame the beast(馴服野獸?不太懂),增加可持續性,你可以在AppDelegate中簡單地聲明一個共享的NSURLCache對象,像這樣:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:2 * 1024 * 1024
                                              diskCapacity:100 * 1024 * 1024
                                              diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];

設置NSURLRequest對象的緩存策略

NSURLCache 將對每一個NSURLRequest對象遵守緩存策略(NSURLRequestCachePolicy),策略如下所示:

- NSURLRequestUseProtocolCachePolicy                默認的緩存策略,對特定的URL請求使用網絡協議中實現的緩存邏輯
- NSURLRequestReloadIgnoringLocalCacheData          忽略本地緩存,重新請請求
- NSURLRequestReloadIgnoringLocalAndRemoteCacheData 忽略本地和遠程緩存,重新請求
- NSURLRequestReturnCacheDataElseLoad               有緩存則從中加載,如果沒有則去請求
- NSURLRequestReturnCacheDataDontLoad               無網絡狀態下不去請求,一直加載本地緩存數據無論其是否存在
- NSURLRequestReloadRevalidatingCacheData           默從原始地址確認緩存數據的合法性之後,緩存數據才可使用,否則請求原始地址

用NSURLCache緩存數據到磁盤

Cache-Control HTTP Header

Cache-Controlheader或Expires header存在於服務器返回的HTTP response header中,來用於客戶端的緩存工作(前者優先級要高於後者),這裡面有很多地方需要注意,Cache-Control可以擁有被定義為類似max-age的參數(在更新響應之前要緩存多長時間), public/private 訪問或者是non-cache(不緩存響應數據),這裡對HTTP cache headers進行了很好的介紹。

繼承並控制NSURLCache

如果你想跳過Cache-Control,並且想要自己來制定規則讀寫一個帶有NSURLResponse對象的NSURLCache,你可以繼承NSURLCache。下面有個例子,使用 CACHE_EXPIRES來判斷在獲取源數據之前對緩存數據保留多長時間.(感謝 Mattt Thompson的回復)

 @interface CustomURLCache : NSURLCache

static NSString * const CustomURLCacheExpirationKey = @"CustomURLCacheExpiration";
static NSTimeInterval const CustomURLCacheExpirationInterval = 600;

@implementation CustomURLCache

+ (instancetype)standardURLCache {
    static CustomURLCache *_standardURLCache = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _standardURLCache = [[CustomURLCache alloc]
                                 initWithMemoryCapacity:(2 * 1024 * 1024)
                                 diskCapacity:(100 * 1024 * 1024)
                                 diskPath:nil];
    }

    return _standardURLCache;
}

#pragma mark - NSURLCache

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    NSCachedURLResponse *cachedResponse = [super cachedResponseForRequest:request];

    if (cachedResponse) {
        NSDate* cacheDate = cachedResponse.userInfo[CustomURLCacheExpirationKey];
        NSDate* cacheExpirationDate = [cacheDate dateByAddingTimeInterval:CustomURLCacheExpirationInterval];
        if ([cacheExpirationDate compare:[NSDate date]] == NSOrderedAscending) {
            [self removeCachedResponseForRequest:request];
            return nil;
        }
    }
}

    return cachedResponse;
}

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse
                 forRequest:(NSURLRequest *)request
{
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:cachedResponse.userInfo];
    userInfo[CustomURLCacheExpirationKey] = [NSDate date];

    NSCachedURLResponse *modifiedCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy];

    [super storeCachedResponse:modifiedCachedResponse forRequest:request];
}
@end

現在你有了屬於自己的NSURLCache的子類,不要忘了在AppDelegate中初始化並且使用它。

在緩存之前重寫NSURLResponse

-connection:willCacheResponse 代理方法是在被緩存之前用於截斷和編輯由NSURLConnection創建的NSURLCacheResponse的地方。 對NSURLCacheResponse進行處理並返回一個可變的拷貝對象(代碼來自NSHipster blog)

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy];
    NSMutableData *mutableData = [[cachedResponse data] mutableCopy];
    NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;

    // ...

    return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
                                                    data:mutableData
                                                userInfo:mutableUserInfo
                                           storagePolicy:storagePolicy];
}

// If you do not wish to cache the NSURLCachedResponse, just return nil from the delegate function:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    return nil;
}

禁用NSURLCache

不想使用NSURLCache?不為所動?好吧,你可以禁用NSURLCache,只需要將內存和磁盤空間設置為0就行了.

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0
                                              diskCapacity:0
                                              diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];

總結

我寫這篇博客的目的是為iOS社區貢獻綿薄之力,並總結了我是如何來處理關於AFNetworking緩存問題的。我們有個內部App在加載了大量圖片後,出現了內存和性能問題,而我的主要職責是診斷這個App的緩存行為,在研究過程中,我在網上搜索了很多資料並且做了很多調試,在我匯總之後就寫到了這篇博客中,我希望這篇文章可以為開發者使用AFNetworking時提供一些幫助,真心希望對你們有用!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved