如何使用NSURLSession和NSURLCache进行caching。 不工作

我有一个testing应用程序的设置,即使用户在下载过程中切换应用程序,也可以从networking上成功下载内容。 太棒了,现在我有了背景下载。 现在我想添加caching。 我不止一次下载图像,系统devise的B / C,给出一个图像的URL我可以告诉你,该url后面的内容将永远不会改变。 所以,现在我想caching我下载的结果,使用苹果公司内置的内存/磁盘caching,我已经读了这么多(而不是我在NSCachesDirectory手动保存文件,然后检查之前,请求,ick)。 为了获得caching在这个工作代码之上的工作,我添加了下面的代码:

- (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; } 

当我创build会话时,我添加了两个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; } 

然后,为了试图看到caching成功,我只是为了超级冗余而切换了我的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 

现在,当我第二次去下载这个项目的时候,这个体验就像第一个一样! 需要很长时间下载和进度栏像原始下载animation缓慢和稳定。 我想立即在caching中的数据! 我错过了什么?

—————————- 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 */ } 

这很棒。 它确认caching正在增长。 我认为,因为我正在使用downloadTask(下载到文件而不是内存),那么这就是为什么DiskCache正在增长,而不是第一次使用内存caching? 我觉得一切都会去内存caching,直到溢出,然后磁盘caching将被使用,也许内存caching写入磁盘之前,操作系统杀死在后台的应用程序释放内存。 我误解了苹果的caching如何工作?

这是肯定的一步,但是第二次下载文件只要第一次 (也许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? } 

你对我的编辑做了什么? 为什么在后续请求中没有看到文件很快返回?

如何确认文件是否从第二个请求的caching中提供?

请注意,以下SOpost帮助我解决了我的问题: NSURLCache是​​否持续启动?

 - (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)行外,还要注意我的caching大小; 500MB。 您需要的caching大小比您要caching的大。 因此,例如,如果您希望能够caching10MB的图像,那么10MB甚至20MB的caching大小是不够的。 如果你需要能够caching10MB,我build议100MBcaching。 希望这可以帮助!! 对我来说有诡计了。

解决scheme – 首先得到所有信息你需要这样的东西

 - (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; } 

在你需要下载每个文件之后 – 以我的情况 – parsing响应并下载图像。 在提出请求之前,您还需要检查caching是否已经对您的请求做出响应 – 就像这样

 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提到的并由Applelogging

NSURLSession不会尝试caching大于caching大小5%的文件

结果就像

在这里输入图像说明

一旦你设置了caching和会话,你应该使用会话方法来下载你的数据:

 - (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 } 

第一次调用后,图像被caching。