NSURLCache:不一致的行为 [英] NSURLCache: inconsistent behaviour

查看:85
本文介绍了NSURLCache:不一致的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我观察到我的应用程序有些奇怪的行为,有时是缓存响应,有时是不缓存它们(所有响应都有Cache-Control:max-age = 600).

I was observirng some strange behaviour of my app sometime caching responses and sometime not caching them (all the responses have Cache-Control: max-age=600).

测试很简单:我做了一个test.php脚本,它只是设置标题并返回一个简单的JSON:

The test is simple: I did a test.php script that was just setting the headers and returning a simple JSON:

<?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'); ?>'" }
}

这是我从PHP页面获得的响应:

This is the response I get from the PHP page:

HTTP/1.1 200 OK
Date: Thu, 28 Nov 2013 11:41:55 GMT
Server: Apache
X-Powered-By: PHP/5.3.17
Cache-Control: max-age=600
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json

{
    "result": {
        "employeeId": "",
        "dateTime": "2013-11-28 11:41:55'" 
    }
}

然后,我创建了一个简单的应用程序并添加了AFNetworking库.

Then I've created a simple app and added AFNetworking library.

当我用很少的参数调用脚本时,缓存可以正常工作:

When I call the script with few parameters, the cache works properly:

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);
}];

但是当我增加参数数量时,例如:

But when I increase the number of parameters, like:

NSDictionary *params = @{
                         @"organizationId": @"4011",
                         @"organizationId2": @"4012",
                         @"organizationId3": @"4013",
                         @"organizationId4": @"4014",
                         @"organizationId5": @"4015",
                         @"organizationId6": @"4016",
                         @"eId": self.firstTest ? @"1" : @"0",
                         @"status": @"2031",
                         };

它不再起作用,并且每次被调用时都会执行一个新请求.

it doesn't work anymore and it execute a new request each time it is called.

我已经进行了许多测试,在我看来,这与URL的长度有关,因为如果我包括以下这组参数:

I've done many tests and it seems to me that it is related to the length of the URL, because if I includes this set of params:

NSDictionary *params = @{
                         @"oId": @"4011",
                         @"oId2": @"4012",
                         @"oId3": @"4013",
                         @"oId4": @"4014",
                         @"oId5": @"4015",
                         @"oId6": @"4016",
                         @"eId": self.firstTest ? @"1" : @"0",
                         @"status": @"2031",
                         };

行得通!

我做了很多测试,这是我发现的唯一模式...

I've done many tests and that's the only pattern I've found...

为了从方程式中排除AFNetworking,我创建了另一个仅使用NSURLConnection的测试程序,并且可以看到相同的行为,因此它不是AFNetworking,也绝对不是NSURLCache.这是另一个测试:

To exclude AFNetworking from the equation, I've created another test program that uses NSURLConnection only and I can see the same behaviour so it's not AFNetworking and definitely NSURLCache. This is the other test:

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中有多少个字符会触发问题,但是即使在这种情况下,我也得到了奇怪的结果:

I've also tried to establish how many characters in the URL will trigger the problem but even in this case I've got strange results:

此字符长度为112个字符,它不起作用:

This one is 112 characters long and it doesn't work:

http://www.mydomain.co.uk/test.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&orgaId4=4

此字符长111个字符,并且可以正常工作:

This one is 111 characters long and it works:

http://www.mydomain.co.uk/test.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&orgId4=4

我已经重命名了PHP脚本,以查看URL的第一部分是否重要,而我又一次遇到了奇怪的行为:

Ive renamed the PHP script to see if the first part of the URL would matter and I've got a strange behaviour again:

此字符长106个字符,它不起作用:

This one is 106 characters long and it doesn't work:

http://www.mydomain.co.uk/t.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&org=40

此字符长105个字符,并且可以正常工作:

This one is 105 characters long and it works:

http://www.mydomain.co.uk/t.php?eId=1&organizationId=4011&organizationId2=4012&organizationId3=4013&org=4

因此,我从页面名称中删除了3个字符,并且将工作阈值降低了6个字符.

So I've removed 3 characters from the page name and I've got a working threshold 6 characters lower.

有什么建议吗?

谢谢, 演示

推荐答案

我看到某些类似的响应未由NSURLCache缓存,并且我想出了另一个可能的原因:

I am witnessing something similar with certain responses not being cached by NSURLCache and I have come up with another possible reason:

就我而言,我已经确定未缓存的响应是使用 Chunked transfer-encoding 返回的响应.我在其他地方读过,NSURLCache应该在iOS 6之后缓存那些内容,但是由于某种原因,在我的情况下(iOS 7.1和8.1),它不是.

In my case I have been able to ascertain that the responses not being cached are the ones that are returned using Chunked transfer-encoding. I've read elsewhere that NSURLCache should cache those after iOS 6 but for some reason it doesn't in my case (iOS 7.1 and 8.1).

我看到这里显示的示例响应也具有Transfer-Encoding: chunked标头.

I see that your example response shown here, also has the Transfer-Encoding: chunked header.

可能是您的某些响应以分块编码(未缓存的)返回而有些则没有(缓存的)吗?

Could it be that some of your responses are returned with chunked encoding (those that are not cached) and some are not (those that are cached)?

我的后端也在Apache上运行PHP,但我仍然不知道为什么要这么做. 可能是一些Apache扩展...

My back-end is also running PHP on Apache and I still can't figure out why it does that... Probably some Apache extension...

无论如何,我认为这听起来比请求URL长度情况更合理.

Anyway, I think it sounds more plausible than the request URL length scenario.

已经有一段时间了,但是我最终可以确认,在我们的例子中,正是分块传输编码导致响应没有被缓存.我已经使用iOS 7.1、8.1、8.3和8.4进行了测试.

It's been a while, but I can finally confirm that in our case, it is the chunked transfer encoding that causes the response not to be cached. I have tested that with iOS 7.1, 8.1, 8.3 and 8.4.

由于我了解到更改服务器上的设置并不总是那么容易,因此,对于使用AFNetworking 2并为 AFHTTPSessionManager 子类化的用户,我有一个建议的解决方案.

Since I understand that it is not always easy to change that setting on your server, I have a solution to suggest, for people who are using AFNetworking 2 and subclassing AFHTTPSessionManager.

您可以将您的子类添加为AFNetworking的 AFNetworkingTaskDidCompleteNotification 的观察者,该子类包含您自己缓存响应所需的所有内容.这意味着:会话数据任务,响应对象和响应数据在响应序列化程序处理之前.

You could add your sub-class as an observer for AFNetworking's AFNetworkingTaskDidCompleteNotification, which contains all the things you will need to cache the responses yourself. That means: the session data task, the response object and the response data before it has been processed by the response serializer.

如果您的服务器仅对部分响应使用分块编码,则可以在-(void)didCompleteTask:中添加代码以仅选择性地缓存响应.因此,例如,您可以检查传输编码响应标头,或根据其他条件缓存响应.

If your server uses chunked encoding for only a few of its responses, you could add code in -(void)didCompleteTask: to only cache responses selectively. So for example you could check for the transfer-encoding response header, or cache the response based on other criteria.

下面的示例HTTPSessionManager子类将缓存所有返回任何数据的响应:

The example HTTPSessionManager sub-class below caches all responses that return any data:

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似乎没有提供回调或委托方法来返回我们需要的所有内容,即在响应序列化程序将其序列化之前.

I tried to come up with some sort of cleaner solution, but it seems that AFNetworking does not provide a callback or a delegate method that returns everything we need early enough - that is, before it has been serialized by the response serializer.

希望人们会发现这很有帮助:)

Hope people will find this helpful :)

这篇关于NSURLCache:不一致的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆