0

我正在尝试在 iOS 上实现以下目标:

  • 始终将本地文件加载到UIWebView静态资产(.html、.js 等)
  • 允许更新协议,以便在一段时间后我们可以为相同的 URL 返回一组不同的静态资产

最小示例的下载链接

我有这个工作,但似乎NSURLCache有时完全错过(可重现),解决这个问题的唯一可靠方法是在正在加载的页面上使用讨厌的缓存破坏技巧。另一个同样令人讨厌的黑客攻击是摧毁UIWebView并创造另一个。

作为一个例子,我们有三个版本的 webapp(分别为 v1、v2 和 v3 的红色、蓝色和绿色):

v1v2v3

每个屏幕由单个 HTML 和 JS 文件组成。

index.html
----------

<!DOCTYPE html>
<html>
<head>
</head>

<body style="background-color:#FF0000">
    <h1 id="label" style="color:#FFFFFF"></h1>
    <script src="app.js"></script>
</body>
</html>

app.js
------
var label = document.getElementById('label');
label.innerHTML = 'red';

每 2 秒发生以下情况:

  1. 我们更改NSURLCache将返回的文件以使其返回不同的版本(注意我们通过覆盖cachedResponseForRequest:而不是实现这一点storeCachedResponse:forRequest:
  2. UIWebView加载一个虚拟页面“ http://www.cacheddemo.co.uk/index.html

逻辑被NSURLCache简单地实现为旋转NSMutableArray

@implementation ExampleCache

- (id)init
{
    self = [super initWithMemoryCapacity:8 * 1024 * 1024 diskCapacity:8 * 1024 * 1024 diskPath:@"webcache.db"];
    if(self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(swapCache:) name:@"swapCache" object:nil];
        self.cache = [@[@{@"index.html":@"index1.html", @"app.js":@"app1.js"},
                       @{@"index.html":@"index2.html", @"app.js":@"app2.js"},
                       @{@"index.html":@"index3.html", @"app.js":@"app3.js"}] mutableCopy];
    }

    return self;
}

- (void)swapCache:(NSNotification *)notification
{
    [self.cache addObject:self.cache[0]];
    [self.cache removeObjectAtIndex:0];
}

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
    NSString *file = [[[request URL] pathComponents] lastObject];
    NSString *mimeType;

    if([file hasSuffix:@".html"]) {
        mimeType = @"text/html";
    } else if([file hasSuffix:@".js"]) {
        mimeType = @"application/javascript";
    }

    if(mimeType) {
        NSString *cachedFile = self.cache[0][file];
        NSUInteger indexOfDot = [cachedFile rangeOfString:@"."].location;

        NSString *path = [[NSBundle mainBundle] pathForResource:[cachedFile substringToIndex:indexOfDot] ofType:[cachedFile substringFromIndex:indexOfDot + 1] inDirectory:@"www"];
        NSData *data = [NSData dataWithContentsOfFile:path];

        if(data.length) {
            NSLog(@"Response returned for %@", file);

            NSURLResponse *urlResponse = [[NSURLResponse alloc] initWithURL:[request URL] MIMEType:mimeType  expectedContentLength:data.length textEncodingName:nil];
            NSCachedURLResponse *response = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:data];
            return response;
        }
    }

    NSLog(@"No response for %@ - %@", file, request);
    return nil;
}

视图控制器逻辑使用 GCDUIWebView在延迟后重新加载:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    [self.webView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
    [self.webView setDelegate:self];
    [self.view addSubview:self.webView];

    [self loadContent];
}

- (void)loadContent
{
    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%d", @"http://www.cacheddemo.co.uk/index.html", arc4random()]]]];
//    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.cacheddemo.co.uk/index.html"]]];

    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [[NSNotificationCenter defaultCenter] postNotificationName:@"swapCache" object:nil];
        [self loadContent];
    });
}

我在这里无法理解的部分是添加查询字符串将使页面重新加载完美无缺(加载 R - G - B - R - G - ...) - 这是未注释的行:

[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%d", @"http://www.cacheddemo.co.uk/index.html", arc4random()]]]];

一旦我们摆脱了查询字符串,NSURLCache除了第一个请求之外的停止被击中,所以它只停留在 R 页面上:

[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.cacheddemo.co.uk/index.html"]]];

查询字符串导致NSURLCache它按预期行事的事实向我表明,浏览器缓存正在以某种方式进行干扰。在我的脑海中,我认为缓存级别是这样工作的:

  1. 检查浏览器缓存
  2. 如果到目前为止没有返回 - 检查NSURLCache
  3. 如果到目前为止没有返回 - 检查代理服务器缓存
  4. 最后尝试加载远程资源

我们如何才能完全禁用浏览器缓存,以便完全控制UIWebView. 不幸的是,我没有看到设置Cache-Control标题的选项NSURLCache- 我已经尝试返回NSHTTPURLResponse带有标题集的 a ,但这似乎被忽略了。

4

2 回答 2

0

浏览器似乎在网络层的缓存(即 NSURLCache)之上有自己的“缓存”。

不确定设置缓存控制标头是否会解决它,但如果您想尝试,您可以在缓存响应ForRequest 的代码中这样做。在创建 NSURLResponse 对象的地方,您可以使用 initWithURL:statusCode:HTTPVersion:headerFields: 来设置标头字段(包括缓存控制标头)。在您的缓存控制标头中,您可以尝试同时使用 no-store 和 no-cache(例如http://blog.55minutes.com/2011/10/how-to-defeat-the-browser-back-button-cache/) . 如果您尝试这样做,请让我们知道它是否有效。

然而,在实践中,我认为最实用和可维护的解决方案是在 URL 上使用数字(即缓存清除)。顺便说一句,您可以使用一个简单的增量代替随机数,该增量在分配 webview 时会重置。

于 2013-09-11T16:10:45.457 回答
0

我不确定我是否理解正确,但缓存控制必须在服务器端设置,例如无缓存、过期等,而不是在 iOS 端。

其次,通过修改查询字符串,即www.mysite.com/page?id=whatever...iOS和任何浏览器认为请求是不一样的,如果你用一些数据库编辑器打开缓存本身,你应该看到一个请求,这是一个数据库条目,对于每个更改的查询.

这种添加随机查询字符串的技巧对于避免浏览器缓存 javascript 文件非常有用。

我希望我能正确理解你的问题。

于 2013-09-10T14:22:23.853 回答