NSURLCache - Кэширование диска для GET-запроса с параметрами, не работающими
Я пытаюсь кэшировать некоторые HTTP-запросы на диск с помощью NSURLCache. Проведя некоторые тесты с AFNetworking и не работая, я создал простой пример, используя NSURLRequest.
Я инициализирую кэш в AppDelegate следующим образом, устанавливая размер памяти равным 0, заставляя его всегда переходить на диск:
NSUInteger sz = 1024*1024*20;
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:sz diskPath:nil];
[NSURLCache setSharedURLCache:cache];
Я использую следующий код для хранения ответа:
NSURLCache * urlCache = [NSURLCache sharedURLCache];
NSString *urlString = @"http://localhost:8000/cities/api/cities/?lang=en";
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSURL *finalUrl = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:finalUrl];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
NSData *data = nil;
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[urlCache storeCachedResponse:cachedResponse forRequest:request];
Мой запрос имеет HTTP-заголовок Cache-Control, например:
response ['cache-control'] = 'max-age = 36000, public'
Я вижу, что ответ кэшируется в файле Cache.db.
А позже, при следующем выполнении или даже в том же, я использую следующий код, чтобы попытаться получить ответ из кэша:
NSCachedURLResponse *cachedResponse = [urlCache cachedResponseForRequest:request];
response = cachedResponse.response;
data = cachedResponse.data;
Проблема в том, что cachedResponse всегда имеет значение null, когда в запросе есть параметры GET. Если запрос " http://localhost:8000/cities/api/cities/", результат сохраняется и восстанавливается позже. Но когда у него есть GET-параметры, что-то мешает методу [NSURLCache cachedResponseForRequest:request] найти запрос.
Получив это, я вложил в класс NSURLCache и реализовал этот метод следующим образом:
- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path {
self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path];
if (self) {
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *dir = (NSString*)[docPaths objectAtIndex:0];
dir = [dir stringByAppendingString:@"/uk.co.airsource.TestURLCache/nsurlcache"];
NSString *path = [dir stringByAppendingFormat:@"/Cache.db"];
self.db = [[FMDatabase alloc] initWithPath:path];
[self.db open];
int o = 4;
}
return self;
}
- (NSCachedURLResponse *) cachedResponseForRequest:(NSURLRequest *)request {
NSCachedURLResponse *cachedURLResponse = [super cachedResponseForRequest:request];
if (cachedURLResponse == nil && [request.HTTPMethod isEqualToString:@"GET"]) {
//[db executeQuery:@"select * from test where a = ?", @"hi'", nil];
NSLog(@"%@", request.URL.absoluteString);
FMResultSet *cfurl_cache_response = [self.db executeQuery:@"select * from cfurl_cache_response where request_key = ? limit 1", request.URL.absoluteString, nil];
if ([cfurl_cache_response next]) {
id entry_ID = [cfurl_cache_response objectForColumnName:@"entry_ID"];
[cfurl_cache_response close];
if (entry_ID != [NSNull null]) {
FMResultSet *cfurl_cache_blob_data = [self.db executeQuery:@"select * from cfurl_cache_blob_data where entry_ID = ? limit 1", entry_ID, nil];
if ([cfurl_cache_blob_data next]) {
id response_object = [cfurl_cache_blob_data objectForColumnName:@"response_object"];
[cfurl_cache_blob_data close];
FMResultSet *cfurl_receiver_data = [self.db executeQuery:@"select * from cfurl_cache_receiver_data where entry_ID = ? limit 1", entry_ID, nil];
if ([cfurl_receiver_data next]) {
id receiver_data = [cfurl_receiver_data objectForColumnName:@"receiver_data"];
[cfurl_receiver_data close];
if (response_object != [NSNull null] && receiver_data != [NSNull null] && response_object && receiver_data) {
NSURLResponse *urlResponse = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[[request allHTTPHeaderFields] objectForKey:@"Accept"] expectedContentLength:[(NSData *)response_object length] textEncodingName:nil];
cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:receiver_data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
}
}
}
}
}
}
return cachedURLResponse;
}
В этом случае при запросе GET с параметрами он следует за каждой строкой до получения NSURLResponse, который был кеширован ранее.
Любые идеи о том, почему он не работает нормально, когда запрос имеет параметры GET?
3 ответа
Наконец, я переопределил NSURLCache, чтобы получить то, что искал:
- GET-запросы (с параметрами) кэшируются на диск
- Запрашивает отправку в кэш, в то время как значение "Cache-Control max-age" говорит о том, что он не истек, и повторное обращение к серверу после истечения срока действия, сохранение нового ответа и начало срока действия по истечении
- В автономном режиме всегда возвращать ответ (если он существует) из кэша
NTURLCache - переопределяет NSURLCache
Я серьезно думаю, что это проблема, которая не работает должным образом в iOS 8.1
Проблема, по-видимому, заключается в том, чтобы не указывать объем памяти, если указать объем памяти следующим образом:
Здесь я указываю объем памяти, равный 1 МБ
[[NSURLCache alloc] initWithMemoryCapacity:1*1024*1024 diskCapacity:sz diskPath:nil];
это будет работать, мне кажется, что объем памяти необходим независимо от того, насколько она мала, но не может быть пустой. Тогда вы сможете извлечь сохраненный кэшированный следующим образом:
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
response = cachedResponse.response;
data = cachedResponse.data;
Проблема сохраняется для iOS 11.x
также, хотя у меня есть iPad Mini под управлением версии iOS 9.3.5
который имеет отлично работающий кеш. Что-то определенно сломано NSURLCache/URLCache