【问题标题】:iOS 7 UIWebView 304 cache bug, blank pagesiOS 7 UIWebView 304 缓存错误,空白页
【发布时间】:2014-03-13 03:47:22
【问题描述】:

我在具有 UIWebView 的应用中发现了一个问题。 iOS 7 缓存了一个空白的 body 304 响应,导致在用户刷新 UIWebView 时显示空白页面。这不是很好的用户体验,我正试图弄清楚如何在 iOS 端解决这个问题,因为我无法控制 Amazon S3 如何响应标头(这是我用于资源托管的人)。

这些人发现了这个错误的更多细节:http://tech.vg.no/2013/10/02/ios7-bug-shows-white-page-when-getting-304-not-modified-from-server/

对于如何在应用端而非服务器端解决此问题提供的任何帮助,我将不胜感激。

谢谢。

更新:使用赏金的建议作为指导修复了此错误:

@property (nonatomic, strong) NSString *lastURL;

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    if ([self.webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"].length < 1)
    {
        NSLog(@"Reconstructing request...");
        NSString *uniqueURL = [NSString stringWithFormat:@"%@?t=%@", self.lastURL, [[NSProcessInfo processInfo] globallyUniqueString]];
        [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uniqueURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]];
    }
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    self.lastURL = [request.URL absoluteString];
    return YES;
}

【问题讨论】:

  • 评论:这也适用于任何其他网站,而不仅仅是 S3。根本问题是我们无法控制的 UIWebView 的内部缓存。
  • 使用标记的答案建议修复了错误
  • 如果这个 URL 已经有一个查询参数,请小心在你的缓存破坏 URL 添加前加上 &!
  • 如果您在发出第二个请求时明确忽略本地和远程缓存,为什么还要添加“?t=”查询参数?
  • @user1888440 可能是因为 iOS 7 忽略了它.. 因此问题的目的。

标签: ios iphone objective-c ios7 uiwebview


【解决方案1】:

由于其他问题是每次都使用 NSURLConnection ,这似乎有点开销: 为什么不在页面加载(完整或不完整)后执行一个小的 javascript,它可以告诉你页面是否实际显示?查询应该存在的标签(比如你的内容 div)并使用

返回真/假
[UIWebView stringByEvaluatingJavaScriptFromString:@"document.getElementById('adcHeader')!=null"]

然后,如果返回 false,您可以使用您自己描述的缓存中断技术手动重新加载 URL:

NSString *uniqueURL = [NSString stringWithFormat:@"%@?t=%d", self.url, [[NSDate date] timeIntervalSince1970]]; 
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uniqueURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]];

[编辑]

根据 cmets 中的讨论和其他一些答案,我认为您可能拥有手动更改 NSURLCache 的最佳解决方案。

根据我收集到的信息,您主要是在尝试解决重新加载/重新显示的情况。在这种情况下,查询NSURLCache 是否有正确的响应,如果没有,则在重新加载UIWebView 之前删除存储的值。

[编辑 2]

根据您的新结果,尝试删除损坏的NSURLCache

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache]cachedResponseForRequest:request];

  if (cachedResponse != nil && [[cachedResponse data] length] > 0)
  {
      NSLog(@"%@",cachedResponse.response);
  } else {
    [[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
  }

  return YES;
}

如果缓存再次无效,我们可能需要改进检查,但理论上这应该可以解决问题!

【讨论】:

  • 这不是很好的体验,因为在我们发起缓存中断请求之前,UIWebView 仍然会显示为空白。整个想法是永远不要显示这个空白页
  • 等等,你每次都会这样吗?从我在链接页面中阅读的内容来看,似乎只有在下载内容时连接中断时才会发生这种情况。在这种情况下,您需要做出权衡:总是中断缓存,从而使您的应用程序变慢,或者仅在必要时重新加载页面,从而为绝大多数用户提供更好的用户体验。
  • 由于用户喜欢刷新按钮,因此很容易复制此错误。
  • 我的经验:这是一个典型的测试用例,用户不会经常使用。实际上,在这些情况下,他们不会介意更长的加载时间。从我目前看到的情况来看,始终针对 90% 进行优化,而不是针对 10% 进行优化 - 这就是您正在做的事情!
  • 这就是问题所在,在这种情况下不再加载,由于糟糕的 iOS 7 错误,UIWebView 只会显示一个丑陋的白页
【解决方案2】:

你可以实现一个NSURLProtocol,然后在+canonicalRequestForRequest:修改请求以覆盖缓存策略。

这适用于所有发出的请求,包括 Web 视图中通常不与公共 API 委托协商的静态资源。

这是非常强大的,但也很容易实现。

这里有更多信息: http://nshipster.com/nsurlprotocol/

参考: https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/Classes/NSURLProtocol_Class/Reference/Reference.html


这是一个例子:

@interface NoCacheProtocol : NSURLProtocol

@end

@implementation NoCacheProtocol

+ (void)load
{
    [NSURLProtocol registerClass:[NoCacheProtocol class]];
}

+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
    if ([NSURLProtocol propertyForKey:@“ProtocolRequest” inRequest:theRequest] == nil) {
        return YES;
    }
    return NO;
}

+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)theRequest
{
    NSMutableURLRequest* request = [theRequest mutableCopy];
    [request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
    //Prevent infinite recursion:
    [NSURLProtocol setProperty:@YES forKey:@"ProtocolRequest" inRequest:request];

    return request;
}

- (void)startLoading
{
    //This is an example and very simple load..

    [NSURLConnection sendAsynchronousRequest:self.request queue:[NSOperationQueue currentQueue] completionHandler:^ (NSURLResponse* response, NSData* data, NSError* error) {
        [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [[self client] URLProtocol:self didLoadData:data];
        [[self client] URLProtocolDidFinishLoading:self];
    }];
}

- (void)stopLoading
{
    NSLog(@"something went wrong!");
}

@end

【讨论】:

  • 我不确定如何将它集成到我的 UIWebView 控制器中
  • @troop231 这间接与 Web 视图一起工作。您在系统中注册协议,您的应用程序发出的每个请求都将与协议进行协商。在+canInitWithRequest: 中,您确定是否应针对每个特定请求咨询协议。
  • 好的,我已经开始在我的头文件中添加这个:@interface WebViewController : UIViewController 现在它显示 8 个警告
  • @troop231 你需要实现NSURLProtocol的子类。客户端将由系统自动提供。我会用一个例子来编辑我的答案。
  • 好的,我刚刚创建了空的子类 .h 和 .m 文件。现在我将 WebProtocol.h 导入到我的 WebViewController.m 中,但似乎缺少一些东西。
【解决方案3】:
NSURL *URL = [NSURL URLWithString:@"http://mywebsite.com"];    
NSURLRequest *request = [NSURLRequest requestWithURL:URL
                                             cachePolicy:NSURLRequestReloadIgnoringCacheData 
                                         timeoutInterval:30.0];

[myWebView loadRequest: request];

在创建 NSURLRequest 实例时,可以设置缓存策略。 希望它有效!

【讨论】:

  • 这仅在视图加载时有效。每次发送请求时,我都需要一种方法来执行此操作 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:
  • 这看起来像唯一的解决方案: NSString *uniqueURL = [NSString stringWithFormat:@"%@?t=%@", self.url, [[NSProcessInfo processInfo] globalUniqueString]]; [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:uniqueURL] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:5.0]];
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 2013-09-07
  • 2016-10-14
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
  • 2014-10-22
相关资源
最近更新 更多