我正在尝试在 iOS 上实现以下目标:
- 始终将本地文件加载到
UIWebView
静态资产(.html、.js 等) - 允许更新协议,以便在一段时间后我们可以为相同的 URL 返回一组不同的静态资产
我有这个工作,但似乎NSURLCache
有时完全错过(可重现),解决这个问题的唯一可靠方法是在正在加载的页面上使用讨厌的缓存破坏技巧。另一个同样令人讨厌的黑客攻击是摧毁UIWebView
并创造另一个。
作为一个例子,我们有三个版本的 webapp(分别为 v1、v2 和 v3 的红色、蓝色和绿色):
每个屏幕由单个 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 秒发生以下情况:
- 我们更改
NSURLCache
将返回的文件以使其返回不同的版本(注意我们通过覆盖cachedResponseForRequest:
而不是实现这一点storeCachedResponse:forRequest:
) 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
它按预期行事的事实向我表明,浏览器缓存正在以某种方式进行干扰。在我的脑海中,我认为缓存级别是这样工作的:
- 检查浏览器缓存
- 如果到目前为止没有返回 - 检查
NSURLCache
- 如果到目前为止没有返回 - 检查代理服务器缓存
- 最后尝试加载远程资源
我们如何才能完全禁用浏览器缓存,以便完全控制UIWebView
. 不幸的是,我没有看到设置Cache-Control
标题的选项NSURLCache
- 我已经尝试返回NSHTTPURLResponse
带有标题集的 a ,但这似乎被忽略了。