NSURLProtocol不要求在响应canInitWithRequest之后加载 [英] NSURLProtocol isn't asked to load after YES response to canInitWithRequest

查看:271
本文介绍了NSURLProtocol不要求在响应canInitWithRequest之后加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在注册NSURLProtocol的实现,以对某些网址请求进行自定义处理(我通过在其上设置属性来标记这些请求)。


  1. 创建UI网络视图(启动后首次加载)

  2. 加载内容

  3. 销毁网络视图

  4. 创建新的网络视图li>加载内容

我看到步骤2和5之间的行为明显不同。我的NSURLProtocol实现管理缓存数据,处理请求。如果我在请求中没有检测到我的属性,我会再次检查特定的URL(用于调试)。在任一情况下, canInitWithRequest 将返回 YES



strong>启动后首次加载:

  2014-10-15 11:37:11.403 MYApp [5813:60b ] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x15ebbb40> https://example.com/ 
2014-10-15 11:37:11.404 MYApp [5813:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x15dc8da0> https://example.com/
2014-10-15 11:37:11.409 MYApp [5813:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x15ee5ef0> https://example.com/
2014-10-15 11:37:11.409 MYApp [5813:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x15ee6240> https://example.com/
2014-10-15 11:37:11.410 MYApp [5813:60b] MYURLProtocol initWithRequest:cachedResponse:client:Request:https://example.com/
2014 -10-15 11:37:11.411 MYApp [5813:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x15ee69d0> https://example.com/
2014-10-15 11:37:11.415 MYApp [5813:9c07] MYURLProtocol startLoading载入中< 0x15ee6240> https://example.com/
...一系列资源加载(缓存响应)...
2014-10-15 11:37:12.497 MYApp [5813:60b] MyWebViewController webViewDidFinishLoad:已完成加载

其他人已经指出有多个调用协议的相同资产,这不是一个问题,虽然有趣的是注意每次它被一个新的对象调用,它是传递到 startLoading 。

 

2014-10-15 11:11:27.466 MYApp [5782:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x1727c310> https://example.com/
2014-10-15 11:11:27.467 MYApp [5782:60b] MYURLProtocol canInitWithRequest:请求中的匹配属性:< 0x145b1d90> https://example.com/
2014-10-15 11:11:27.488 MYApp [5782:560f] MYURLProtocol canInitWithRequest:请求中的匹配网址:< 0x17266060> https://example.com/
2014-10-15 11:11:27.669 MYApp [5782:60b] MYWebViewController webViewDidFinishLoad:已完成加载

这是在我看来,行为是意想不到的。看来,第三次传递给 canInitWithRequest 时,属性已被剥离请求,然后,即使我们响应 YES 我们从来没有真正得到inited - 页面被简单地返回到 UIWebView 整体,没有后续的资产请求。这是请求在创建时的样子:

  NSURLRequest * request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval: 60.0]; 
[self.webView loadRequest:request];

为什么,当我说我的协议可以处理请求时,这样做?我的猜测是,答案是在UIWebView本身的实现。

解决方案

我能够使用我的协议


  1. 通过缓存清除UIWebView缓存,而不是清除NSURLCache缓存。原始请求。我选择了'key = 000000',其中的值是零领导的六位随机数。

  2. 在协议中,剥离 canonicalRequestForRequest: initWithRequest:cachedResponse:client

像这样(可能有一个更干净的方法来删除param,但这是工作):

  +(NSURLRequest *)canonicalRequestForRequest: (NSURLRequest *)request 
{
NSURLRequest * canonicalRequest = request;
BOOL myProtocolRequest = [[NSURLProtocol propertyForKey:kMYProtocolRequest inRequest:request] boolValue];
if(myProtocolRequest)
{
NSMutableURLRequest * mutableRequest = [request mutableCopyWorkaround];
NSString * originalURLString = mutableRequest.URL.absoluteString;
NSString * regexString = [NSString stringWithFormat:@(?:[?&])(key = [[:digit:]] {%d}& *),kMYKeyLength];

NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:0];
NSTextCheckingResult * result = [regex firstMatchInString:originalURLString options:0 range:NSMakeRange(0,originalURLString.length)];
if(result.numberOfRanges> 1)
{
NSRange keyRange = [result rangeAtIndex:1];
NSLog(@从请求中删除'%@',[originalURLString substringWithRange:keyRange]);
NSString * replacementURLString = [originalURLString stringByReplacingCharactersInRange:keyRange withString:@];
mutableRequest.URL = [NSURL URLWithString:replacementURLString];
canonicalRequest = mutableRequest;
}
}

return canonicalRequest;
}

我的初始化代码如下所示:



<$ c $ p> - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id< NSURLProtocolClient>)client
{
self = [super initWithRequest:[MYURLProtocol canonicalRequestForRequest:request] cachedResponse:cachedResponse client:client];
return self;
}



我不喜欢我必须这样做,终于得到了我想要的行为。希望它帮助某人在那里。


I'm registering an implementation of NSURLProtocol to do some custom handling of certain URL requests (I tag these requests by setting properties on them). These URL requests are coming from a UIWebView's load method.

  1. Create a UI web view (first load after launch)
  2. Load content
  3. Destroy web view
  4. Create a new web view (subsequent loads)
  5. Load content

I'm seeing significantly different behavior between steps 2 and 5. My NSURLProtocol implementation manages cached data and is designed to handle the requests. If I don't detect my property in a request, I do a second check for a specific URL (for debugging). In either case canInitWithRequest will return YES:

First load after launch:

2014-10-15 11:37:11.403 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ebbb40> https://example.com/ 
2014-10-15 11:37:11.404 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15dc8da0> https://example.com/ 
2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee5ef0> https://example.com/ 
2014-10-15 11:37:11.409 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee6240> https://example.com/ 
2014-10-15 11:37:11.410 MYApp[5813:60b] MYURLProtocol initWithRequest:cachedResponse:client: Request: https://example.com/
2014-10-15 11:37:11.411 MYApp[5813:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x15ee69d0> https://example.com/ 
2014-10-15 11:37:11.415 MYApp[5813:9c07] MYURLProtocol startLoading Loading <0x15ee6240> https://example.com/ 
... A bunch of loading of assets occurs (cached responses) ...
2014-10-15 11:37:12.497 MYApp[5813:60b] MyWebViewController webViewDidFinishLoad: Finished loading

Others have pointed out that there are multiple calls to the protocol about the same asset, and this is not a concern, though it's interesting to note that each time it is called with a new object, and it is the 4th (of 4) object that gets passed to startLoading. Still, no real concerns here.

Subsequent loads:

2014-10-15 11:11:27.466 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x1727c310> https://example.com/ 
2014-10-15 11:11:27.467 MYApp[5782:60b] MYURLProtocol canInitWithRequest: Matched property in request: <0x145b1d90> https://example.com/ 
2014-10-15 11:11:27.488 MYApp[5782:560f] MYURLProtocol canInitWithRequest: Matched URL in request: <0x17266060> https://example.com/
2014-10-15 11:11:27.669 MYApp[5782:60b] MYWebViewController webViewDidFinishLoad: Finished loading

This is where the behavior, in my view, is unexpected. It appears that property has been stripped off of the request by the third time it is passed to canInitWithRequest, and then, even though we respond YES we never actually get inited -- the page is simply returned to the UIWebView in its entirety, with no subsequent requests for assets. Here is what the request looks like when it is created:

NSURLRequest *request = [NSURLRequest requestWithURL:myURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[self.webView loadRequest:request];

Why, when I say that my protocol can handle the request, is it not being given the opportunity to do so? My guess is that the answer is in the implementation of UIWebView itself. Any thoughts on how to work around this if I really want my protocol to be the responsible entity for loading?

解决方案

I was able to workaround this issue by cache-busting the UIWebView cache, while not busting the NSURLCache.

  1. Add a unique param to the query params of the original request. I chose 'key=000000' where the value is zero-led six digit random number.
  2. In the protocol, strip the key in canonicalRequestForRequest: and in initWithRequest:cachedResponse:client

My stripping code looks like this (there might be a cleaner way to strip the param, but this works):

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    NSURLRequest *canonicalRequest = request;
    BOOL myProtocolRequest = [[NSURLProtocol propertyForKey:kMYProtocolRequest inRequest:request] boolValue];
    if (myProtocolRequest)
    {
        NSMutableURLRequest *mutableRequest = [request mutableCopyWorkaround];
        NSString *originalURLString = mutableRequest.URL.absoluteString;
        NSString *regexString = [NSString stringWithFormat:@"(?:[?&])(key=[[:digit:]]{%d}&*)", kMYKeyLength];

        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:0];
        NSTextCheckingResult *result = [regex firstMatchInString:originalURLString options:0 range:NSMakeRange(0, originalURLString.length)];
        if (result.numberOfRanges > 1)
        {
            NSRange keyRange = [result rangeAtIndex:1];
            NSLog(@"Removing '%@' from request", [originalURLString substringWithRange:keyRange]);
            NSString *replacementURLString = [originalURLString stringByReplacingCharactersInRange:keyRange withString:@""];
            mutableRequest.URL = [NSURL URLWithString:replacementURLString];
            canonicalRequest = mutableRequest;
        }
    }

    return canonicalRequest;
}

My init code looks like this:

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{
    self = [super initWithRequest:[MYURLProtocol canonicalRequestForRequest:request] cachedResponse:cachedResponse client:client];
    return self;
}

I don't like that I have to do this, but I'm finally getting exactly the behavior I want. Hopefully it helps someone out there.

这篇关于NSURLProtocol不要求在响应canInitWithRequest之后加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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