我有一个测试应用设置,即使用户在下载过程中切换应用,它也能成功从网络下载内容。太好了,现在我有后台下载了。现在我想添加缓存。没有必要让我多次下载图像,b / c系统设计,给定一个图像URL我可以告诉你该URL后面的内容永远不会改变。所以,现在我想使用Apple内置的内存/磁盘缓存来缓存下载的结果,我已经阅读了很多(而不是我在NSCachesDirectory中手动保存文件,然后在创建之前检查文件)请求,ick)。在尝试中使缓存在此工作代码之上工作,我添加了以下代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Set app-wide shared cache (first number is megabyte value)
[NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:60 * 1024 * 1024
diskCapacity:200 * 1024 * 1024
diskPath:nil]];
return YES;
}
当我创建会话时,我添加了两行NEW(URLCache和requestCachePolicy)。
// Helper method to get a single session object
- (NSURLSession *)backgroundSession
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.example.apple-samplecode.SimpleBackgroundTransfer.BackgroundSession"];
configuration.URLCache = [NSURLCache sharedURLCache]; // NEW LINE ON TOP OF OTHERWISE WORKING CODE
configuration.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad; // NEW LINE ON TOP OF OTHERWISE WORKING CODE
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return session;
}
然后,为了看到缓存成功,我只是为了超冗余,我从
切换了我的NSURLRequest行// NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; // Old line, I've replaced this with...
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:2*60]; // New line
现在,当我第二次下载该项目时,体验就像第一次一样!需要很长时间才能下载和进度条动画缓慢而稳定,就像原始下载一样。我想立即在缓存中的数据!!我错过了什么?
---------------------------- UPDATE ------------------ ----------
好的,感谢Thorsten的回答,我在我的didFinishDownloadingToURL
委托方法中添加了以下两行代码:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL {
// Added these lines...
NSLog(@"DiskCache: %@ of %@", @([[NSURLCache sharedURLCache] currentDiskUsage]), @([[NSURLCache sharedURLCache] diskCapacity]));
NSLog(@"MemoryCache: %@ of %@", @([[NSURLCache sharedURLCache] currentMemoryUsage]), @([[NSURLCache sharedURLCache] memoryCapacity]));
/*
OUTPUTS:
DiskCache: 4096 of 209715200
MemoryCache: 0 of 62914560
*/
}
这很棒。它证实缓存正在增长。我认为,因为我使用的是downloadTask(下载到文件而不是内存),这就是为什么DiskCache正在增长而不是内存缓存?我想一切都会进入内存缓存,直到溢出,然后将使用磁盘缓存,并且可能在操作系统在后台杀死应用程序以释放内存之前,内存缓存已写入磁盘。我误解了Apple的缓存是如何工作的吗?
这是向前迈出的一步,但是第二次下载文件它只需要第一次(可能是10秒左右)并且以下方法再次执行:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// This shouldn't execute the second time around should it? Even if this is supposed to get executed a second time around then shouldn't it be lightning fast? It's not.
// On all subsequent requests, it slowly iterates through the downloading of the content just as slow as the first time. No caching is apparent. What am I missing?
}
你对我上面的编辑有什么看法?为什么我在后续请求中没有看到文件很快返回?
如何在第二次请求时确认文件是否从缓存中提供?
答案 0 :(得分:32)
请注意,以下SO帖子帮助我解决了我的问题:Is NSURLCache persistent across launches?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Set app-wide shared cache (first number is megabyte value)
NSUInteger cacheSizeMemory = 500*1024*1024; // 500 MB
NSUInteger cacheSizeDisk = 500*1024*1024; // 500 MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
sleep(1); // Critically important line, sadly, but it's worth it!
}
除了sleep(1)
行之外,还要注意我的缓存大小; 500MB。
根据docs,您需要的缓存大小比您尝试缓存的大小要多。
响应大小足够小,可以合理地放入缓存中。 (例如,如果提供磁盘缓存,则响应必须为no 大于磁盘缓存大小的5%左右。)
因此,例如,如果您希望能够缓存10MB图像,那么10MB甚至20MB的缓存大小是不够的。你需要200MB。 下面是Honey的评论,证明Apple正遵循这一5%的规则。对于8Mb,他必须将缓存大小设置为最小154MB。
答案 1 :(得分:10)
解决方案 - 首先获取所有信息,你需要这样的东西
- (void)loadData
{
if (!self.commonDataSource) {
self.commonDataSource = [[NSArray alloc] init];
}
[self setSharedCacheForImages];
NSURLSession *session = [self prepareSessionForRequest];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[BaseURLString stringByAppendingPathComponent:@"app.json"]]];
[request setHTTPMethod:@"GET"];
__weak typeof(self) weakSelf = self;
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSArray *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
weakSelf.commonDataSource = jsonResponse;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf updateDataSource];
});
}
}];
[dataTask resume];
}
- (void)setSharedCacheForImages
{
NSUInteger cashSize = 250 * 1024 * 1024;
NSUInteger cashDiskSize = 250 * 1024 * 1024;
NSURLCache *imageCache = [[NSURLCache alloc] initWithMemoryCapacity:cashSize diskCapacity:cashDiskSize diskPath:@"someCachePath"];
[NSURLCache setSharedURLCache:imageCache];
}
- (NSURLSession *)prepareSessionForRequest
{
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
[sessionConfiguration setHTTPAdditionalHeaders:@{@"Content-Type": @"application/json", @"Accept": @"application/json"}];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
return session;
}
在您需要下载每个文件后 - 在我的情况下 - 解析响应并下载图像。在提出请求之前,您还需要检查缓存是否已经响应了您的请求 - 就像这样
NSString *imageURL = [NSString stringWithFormat:@"%@%@", BaseURLString ,sourceDictionary[@"thumb_path"]];
NSURLSession *session = [self prepareSessionForRequest];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
[request setHTTPMethod:@"GET"];
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (cachedResponse.data) {
UIImage *downloadedImage = [UIImage imageWithData:cachedResponse.data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.thumbnailImageView.image = downloadedImage;
});
} else {
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
UIImage *downloadedImage = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.thumbnailImageView.image = downloadedImage;
});
}
}];
[dataTask resume];
}
之后,您还可以使用xCode Network Analyzer检查结果。
同样请注意@jcaron和documented by Apple
NSURLSession不会尝试缓存大于缓存5%的文件 大小
结果类似
答案 2 :(得分:2)
设置缓存和会话后,您应该使用会话方法下载数据:
- (IBAction)btnClicked:(id)sender {
NSString *imageUrl = @"http://placekitten.com/1000/1000";
NSURLSessionDataTask* loadDataTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
UIImage *downloadedImage = [UIImage imageWithData:data];
NSLog(@"ImageSize: %f, %f", downloadedImage.size.width, downloadedImage.size.height);
NSLog(@"DiskCache: %i of %i", [[NSURLCache sharedURLCache] currentDiskUsage], [[NSURLCache sharedURLCache] diskCapacity]);
NSLog(@"MemoryCache: %i of %i", [[NSURLCache sharedURLCache] currentMemoryUsage], [[NSURLCache sharedURLCache] memoryCapacity]);
}];
[loadDataTask resume]; //start request
}
第一次通话后,图像被缓存。