Objective c NSURLCache:不一致的行为
我观察到我的应用程序出现了一些奇怪的行为,有时缓存响应,有时不缓存响应(所有响应都有缓存控制:max age=600) 测试很简单:我编写了一个test.php脚本,它只是设置了头并返回了一个简单的JSON:Objective c NSURLCache:不一致的行为,objective-c,caching,nsurlcache,Objective C,Caching,Nsurlcache,我观察到我的应用程序出现了一些奇怪的行为,有时缓存响应,有时不缓存响应(所有响应都有缓存控制:max age=600) 测试很简单:我编写了一个test.php脚本,它只是设置了头并返回了一个简单的JSON: <?php header('Content-Type: application/json'); header('Cache-Control: max-age=600'); ?> { "result": { "employeeId": "&
<?php
header('Content-Type: application/json');
header('Cache-Control: max-age=600');
?>
{
"result": {
"employeeId": "<?php echo $_GET['eId']; ?>",
"dateTime": "<?php echo date('Y-m-d H:i:s'); ?>'" }
}
然后我创建了一个简单的应用程序并添加了AFNetworking库
当我使用很少的参数调用脚本时,缓存工作正常:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *params = @{
@"oId": @"4011",
@"eId": self.firstTest ? @"1" : @"0",
@"status": @"2031",
};
[manager GET:@"http://www.mydomain.co.uk/test.php" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
NSLog(@"Cache current memory usage (after call): %d", [cache currentMemoryUsage]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
但是当我增加参数的数量时,比如:
NSDictionary *params = @{
@"organizationId": @"4011",
@"organizationId2": @"4012",
@"organizationId3": @"4013",
@"organizationId4": @"4014",
@"organizationId5": @"4015",
@"organizationId6": @"4016",
@"eId": self.firstTest ? @"1" : @"0",
@"status": @"2031",
};
它不再工作了,每次调用它时都会执行一个新的请求
我做了很多测试,在我看来,这与URL的长度有关,因为如果我包括这组参数:
NSDictionary *params = @{
@"oId": @"4011",
@"oId2": @"4012",
@"oId3": @"4013",
@"oId4": @"4014",
@"oId5": @"4015",
@"oId6": @"4016",
@"eId": self.firstTest ? @"1" : @"0",
@"status": @"2031",
};
它起作用了
我做了很多测试,这是我发现的唯一模式
为了从等式中排除AFNetworking,我创建了另一个仅使用NSURLConnection的测试程序,我可以看到相同的行为,因此它不是AFNetworking,而是NSURLCache。这是另一个测试:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.mydomain.co.uk/test.php?eId=%@&organizationId=4011&organizationId2=4012&organizationId3=4013&organizationId4=4014&organizationId5=4015&organizationId6=4016", self.firstTest ? @"1" : @"0"]]; // doesn't work
//NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.mydomain.co.uk/test.php?eId=%@&oId=4011&oId2=4012&oId3=4013&oId4=4014&oId5=4015&oId6=4016", self.firstTest ? @"1" : @"0"]]; // work
//NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.mydomain.co.uk/test.php?eId=%@", self.firstTest ? @"1" : @"0"]]; // work
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error == nil) {
// Parse data here
NSString *responseDataStr = [NSString stringWithUTF8String:[data bytes]];
NSLog(@"Response data: %@", responseDataStr);
}
我还试图确定URL中有多少字符会引发问题,但即使在这种情况下,我也得到了奇怪的结果:
这一个有112个字符长,不起作用:
这一个有111个字符长,可以工作:
我重新命名了PHP脚本,看看URL的第一部分是否重要,我又有了一个奇怪的行为:
这一个有106个字符长,不起作用:
这一个是105个字符长,它的作品:
因此,我从页面名称中删除了3个字符,并将工作阈值降低了6个字符
有什么建议吗
谢谢,
Dem您是否尝试配置
NSURLRequestCachePolicy
对于NSURLRequest
+ (id)requestWithURL:(NSURL *)theURL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
这些常量用于指定与缓存响应的交互
enum
{
NSURLRequestUseProtocolCachePolicy = 0,
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4,
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5
};
typedef NSUInteger NSURLRequestCachePolicy;
您是否尝试配置
NSURLRequestCachePolicy
对于NSURLRequest
+ (id)requestWithURL:(NSURL *)theURL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
这些常量用于指定与缓存响应的交互
enum
{
NSURLRequestUseProtocolCachePolicy = 0,
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4,
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5
};
typedef NSUInteger NSURLRequestCachePolicy;
您可以通过子类化NSURLProtocol并覆盖惊人的加载来调查sharedURLCache中缓存的响应是什么: 外接程序AppDelegate应用程序:didFinishLaunchingWithOptions:
[NSURLProtocol registerClass:[CustomURLProtocol class]];
然后创建NSURLProtocol(CustomURLProtol)的子类,并覆盖惊人加载
- (void)startLoading
{
self.cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
if (self.cachedResponse) {
[self.client URLProtocol:self
didReceiveResponse:[self.cachedResponse response]
cacheStoragePolicy:[self.cachedResponse storagePolicy]];
[self.client URLProtocol:self didLoadData:[self.cachedResponse data]];
}
[self.client URLProtocolDidFinishLoading:self];
}
self.cachedResponse是我添加的nsCachedUrResponse属性。您可以查看此处的任何缓存响应是否有问题。您可以通过子类化NSURLProtocol并覆盖惊人的加载来调查sharedURLCache中的缓存响应: 外接程序AppDelegate应用程序:didFinishLaunchingWithOptions:
[NSURLProtocol registerClass:[CustomURLProtocol class]];
然后创建NSURLProtocol(CustomURLProtol)的子类,并覆盖惊人加载
- (void)startLoading
{
self.cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
if (self.cachedResponse) {
[self.client URLProtocol:self
didReceiveResponse:[self.cachedResponse response]
cacheStoragePolicy:[self.cachedResponse storagePolicy]];
[self.client URLProtocol:self didLoadData:[self.cachedResponse data]];
}
[self.client URLProtocolDidFinishLoading:self];
}
self.cachedResponse是我添加的nsCachedUrResponse属性。您可以看到这里的任何缓存响应是否有问题。我看到一些类似的情况,NSURLCache没有缓存某些响应,我找到了另一个可能的原因: 在我的例子中,我能够确定没有被缓存的响应是使用分块传输编码返回的响应。我在其他地方读到过,NSURLCache应该在iOS 6之后缓存这些内容,但出于某种原因,在我的情况下(iOS 7.1和8.1)没有这样做 我看到这里显示的示例响应也有
Transfer-Encoding:chunked
标题
可能是您的一些响应是使用分块编码返回的(未缓存的)而有些不是(缓存的)吗
我的后端也在Apache上运行PHP,我仍然不明白为什么它会这样做。。。
可能是一些Apache扩展
无论如何,我认为这听起来比请求URL长度场景更合理
编辑:
已经有一段时间了,但我最终可以确认,在我们的例子中,是分块传输编码导致响应无法缓存。我已经用iOS 7.1、8.1、8.3和8.4测试过了 因为我知道在您的服务器上更改该设置并不总是容易的,所以我有一个解决方案建议给使用AFNetworking 2和子类化AFHTTPSessionManager的用户 您可以添加子类作为AFNetworking的AfNetworkingTaskIDCompleteNotification的观察者,它包含您自己缓存响应所需的所有内容。这意味着:会话数据任务、响应对象以及响应序列化程序处理之前的响应数据 如果您的服务器只对少数响应使用分块编码,则可以在-(void)didCompleteTask:中添加代码,以便仅选择性地缓存响应。例如,您可以检查传输编码响应头,或者根据其他条件缓存响应 下面的示例HTTPSessionManager子类缓存返回任何数据的所有响应: MyHTTPSessionManager.h
@interface MyHTTPSessionManager : AFHTTPSessionManager
@end
MyHTTPSessionManager.m
#import "MyHTTPSessionManager.h"
@implementation MyHTTPSessionManager
+ (instancetype)sharedClient {
static MyHTTPClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:_sharedClient selector:@selector(didCompleteTask:) name:AFNetworkingTaskDidCompleteNotification object:nil];
});
return _sharedClient;
}
- (void)didCompleteTask:(NSNotification *)notification {
NSURLSessionDataTask *task = notification.object;
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
NSData *responseData = notification.userInfo[AFNetworkingTaskDidCompleteResponseDataKey];
if (!responseData.length) {
// Do not cache empty responses.
// You could place additional checks above to cache responses selectively.
return;
}
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:responseData];
[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:task.currentRequest];
}
我试图想出一些更干净的解决方案,但似乎AFNetworking没有提供回调或委托方法来尽早返回我们需要的所有内容——也就是说,在响应序列化程序序列化之前
希望人们会发现这一点很有帮助:)我看到一些类似的情况,NSURLCache没有缓存某些响应,我想出了另一个可能的原因: 在我的例子中,我能够确定没有被缓存的响应是使用分块传输编码返回的响应。我在其他地方读到过,NSURLCache应该在iOS 6之后缓存这些内容,但出于某种原因,在我的情况下(iOS 7.1和8.1)没有这样做 我看到这里显示的示例响应也有
Transfer-Encoding:chunked
标题
会不会是这样的