AFNetworking中的缓存是如何工作的?:对AFImageCache & NSUrlCache 解释
2015-04-24 13:31
393 查看
大致翻译自:How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
如果你是个在使用Mattt Thompson’s的
AFImageCache:
NSURLCache:
为了了解每个缓存系统是怎么工作的,让我们来看看它们是怎么定义的:
编辑(03.14.14):Mattt Thompson已通知我,自
不幸的是,it has a tendency to hog memory而且默认配置是不会直接写入到磁盘上。为了tame the beast,并增加可持续性,你可以在app代理中声明一个共享的
这里我们声明了一个共享的
NSURLRequestUseProtocolCachePolicy:对特定的 URL 请求使用网络协议中实现的缓存逻辑。这是默认的策略。
NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存,重新加载
NSURLRequestReloadIgnoringLocalAndRemoteCacheData:忽略本地和远程缓存,重新加载
NSURLRequestReturnCacheDataElseLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么从原始地址加载数据。
NSURLRequestReturnCacheDataDontLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么放弃从原始地址加载数据,请求视为失败(即:“离线”模式)。
NSURLRequestReloadRevalidatingCacheData:从原始地址确认缓存数据的合法性后,缓存数据就可以使用,否则从原始地址加载。
下面一个例子,该例子使用了
(感谢Mattt Thompson的反馈和对代码的编辑)
现在你已经有了NSURLCache的子类,别忘了在AppDelegate初始化时使用它:
如果你是个在使用Mattt Thompson’s的
AFNetworking的开发者,也许你会好奇或者困惑于缓存的机制和你如何调整它,已对自己有利。
AFNetworking实际上用了2种不同的缓存机制:
AFImageCache:
AFNetworking专用的memory-only图像缓存,继承自NSCache
NSURLCache:
NSURLConnection's的默认的URL缓存机制,用来存储
NSURLResponse对象:默认是
in-memory缓存,也可以配置为
on-disk的持久化缓存。
为了了解每个缓存系统是怎么工作的,让我们来看看它们是怎么定义的:
AFImageCache是怎样工作的?
AFImageCache是
UIImageView+AFNetworking类别的一部分。它是
NSCache的子类,把URL字符串作为一个键(从输入的
NSURLRequest对象获得)来存储
UIImage对象。
AFImageCache定义:
@interface AFImageCache : NSCache <AFImageCache> // singleton instantiation : + (id <AFImageCache>)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的private implementation。在
UIImageView+AFNetworking类别之外,无法自定义。它将所有的
UIImage对象存储到它的
NSCache中。
NSCache来控制
UIImage对象什么时候被释放。如果你像监测images什么时候可以被释放掉,你可以实现
NSCacheDelegate的
cache:willEvictObject方法。
编辑(03.14.14):Mattt Thompson已通知我,自
AFNetworking2.1,
AFImageCache是可配置的。现在有个公有的方法setSharedImageCache。这是AFN 2.2.1 的说明UIImageView+AFNetworking specification。
NSURLCache是如何工作的?
由于AFNetworking使用了
NSURLConnection,它充分利用了它原生的缓存机制
NSURLCache。
NSURLCache缓存通过
NSURLConnection返回的的
NSURLResponse对象。
Enabled by Default, but Needs a Hand
NSURLCache的
sharedCache是默认启用的,被用来获取任何
NSURLConnection对象的URL内容。
不幸的是,it has a tendency to hog memory而且默认配置是不会直接写入到磁盘上。为了tame the beast,并增加可持续性,你可以在app代理中声明一个共享的
NSURLCache,如下:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:2 * 1024 * 1024 diskCapacity:100 * 1024 * 1024 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache];
这里我们声明了一个共享的
NSURLCache,占2mb内存,100mb的磁盘空间。
在NSURLRequest对象设置缓存策略
NSURLCache会遵循每个NSURLRequest对象的缓存策略(NSURLRequestCachePolicy)。策略定义如下:NSURLRequestUseProtocolCachePolicy:对特定的 URL 请求使用网络协议中实现的缓存逻辑。这是默认的策略。
NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存,重新加载
NSURLRequestReloadIgnoringLocalAndRemoteCacheData:忽略本地和远程缓存,重新加载
NSURLRequestReturnCacheDataElseLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么从原始地址加载数据。
NSURLRequestReturnCacheDataDontLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么放弃从原始地址加载数据,请求视为失败(即:“离线”模式)。
NSURLRequestReloadRevalidatingCacheData:从原始地址确认缓存数据的合法性后,缓存数据就可以使用,否则从原始地址加载。
使用NSURLCache缓存到磁盘上
Cache-Control HTTP Header
为了在客户端缓存,Cache-Controlheader或者
Expiresheade必须在服务器的HTTP的响应头中(
Cache-Controlheader存在的优先级高于
Expiresheader)。有许多情况要考虑到。 Cache Control可以有定义的参数,例如
max-age(在更新响应之前缓存要多久), public / private access,或者是
no-cache无缓存(不缓存响应)。这儿是对HTTP cache headers的简介。
Subclass NSURLCache for Ultimate Control
如果你想要绕过HTTPCache-Controlheader的要求,想要定义你自己的读写给定
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初始化时使用它:
CustomURLCache *URLCache = [[CustomURLCache alloc] initWithMemoryCapacity:2 * 1024 * 1024 diskCapacity:100 * 1024 * 1024 diskPath:nil]; [NSURLCache setSharedURLCache:URLCache];
在缓存前重写NSURLResponse
在缓存前,NSURLConnection的
-connection:willCacheResponse代理方法是来拦截和编辑
NSURLCachedResponse的地方。为了编辑
NSURLCachedResponse,如下所示返回可编辑的副本(来自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,在你的appDelegate中,把共享的
NSURLCache的内存和磁盘空间置为0即可:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache];
总结
我写这篇博客是为了对iOS社区有益,我总结了所有与AFNetworking缓存有关的信息。我们内部有个app来加载大量的图片,它有一些内存和性能问题。我被安排来诊断app的缓存行为。在练习的过程中,我在网上发现了大量的信息,并做了大量的调试和测试。我希望我总结的信息,可以为其他使用AFNetworking的人提供额外的信息。我希望这对你有所帮助。相关文章推荐
- (译)缓存在AFNetworking中是如何工作的?AFImageCache和NSUrlCache给你答案
- 11、缓存在AFNetworking中是如何工作的?AFImageCache和NSUrlCache给你答案
- (译)缓存在AFNetworking中是如何工作的?AFImageCache和NSUrlCache给你答案
- (译)缓存在AFNetworking中是如何工作的?AFImageCache和NSUrlCache给你答案
- AFNetWorking是如何进行数据缓存的--之AFImageCache & NSURLCache 详解
- How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
- AFNetWorking是如何进行数据缓存的--之AFImageCache & NSURLCache 详解
- How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
- How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
- IE6下背景图片不缓存问题或者document.execCommand("BackgroundImageCache",false,true)
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
- 005 [翻译]SDWebImage和AFNetworking及原生缓存策略的比较
- squid 如何强制缓存 no-cache 头的响应
- AFNetworking & SDWebImage
- ★如何解释特修斯之船问题?
- iOS SDWebimage~SDImageCache计算缓存大小
- 【构建Android缓存模块】(二)Memory Cache & File Cache
- 春节吃家宴,如何向亲戚解释你的工作?
- 如何识别你工作中的各种"人"?