iOS:将自定义NSURL传递给NSURLProtocol

时间:2013-10-30 15:35:03

标签: ios nsurl nsurlrequest nsurlprotocol

我需要传递一些额外的信息以及UIWebView loadRequest:,以便它达到NSURLProtocol的实现。信息无法绑定到NSURLRequest,因为信息也必须与NSURLRequest mainDocumentURL一起保留。所以我将NSURL子类化并用它构造NSURLRequest。我已经知道到达NSURLRequest的{​​{1}}不是我已经提供给NSURLProtocol startLoading的实例,所以我也实现了UIWebView loadRequest,天真地期望URL加载系统会使用它

现在,NSURL copyWithZone不会像人们合理预期的那样被召唤,但至少会在NSURLProtocol canInitWithRequest之前被召唤4次。前两次,传入的startLoading仍然包含我的自定义NSURLRequest实现。然后,一个名为NSURL的不幸内部代码会询问我的自定义CFURLCopyAbsoluteURL的{​​{1}},而下一个absoluteURL(以及之后的NSURL)已经获得了全新canInitWithRequest其中包含新的startLoading {1}}。 <{1}}永远不会被调用,我的子类NSURLRequest也会丢失。

在我放弃并实施一个劣质且脆弱的解决方案并将内容直接附加到URL字符串之前,我想问更高级别的向导,他们是否看到了如何捕获{{1的初始闪烁的方法雷达或如何欺骗NSURL来携带我的自定义实例。我试图通过再次返回我的自定义NSURL类的新实例来破解copyWithZone,但它没有帮助。我在NSURLProtocol setProperty功能中看到了一些承诺,但现在看起来很无用。网址加载系统会愉快地创建所有内容的新实例,NSURL到达NSURLProtocol似乎与偶然输入CFURLCopyAbsoluteURL的{​​{1}}相同。

更新:确定我希望尽可能缩短帖子,但即使是第一个回复也是要求技术背景,所以我们开始:我有多个{{1}在应用程序中。这些视图可以同时运行请求,绝对可以运行相同URL的请求。这就像桌面浏览器中的标签。但我需要区分哪个NSURL absoluteURL是到达NSURLRequest的每个特定NSURLProtocol的来源。我需要每个URL请求都带有一个上下文。我不能简单地将网址映射到数据,因为多个UIWebView可能随时加载相同的网址。

更新2:首选将上下文信息附加到UIWebView,根据我的理解,这是唯一可用的。问题是页面内引用的资源请求(图像等)根本不会通过UIWebView,而是直接在NSURLRequest结束。我没有机会在NSURLProtocol之前的任何地方触摸,检查或修改此类请求。此类请求的唯一上下文链接是UIWebViews

4 个答案:

答案 0 :(得分:3)

如果有某种方法可以将原始NSURL用作mainDocumentURL,那将是理想的选择。如果没有办法阻止它被复制,我想到了以下黑客作为替代方案:

在将每个UIWebViewset the user agent string创建为唯一值之前。据推测,此更改仅影响随后创建的UIWebView个对象,因此每个视图最终都会显示自己独特的用户代理字符串。

NSURLProtocol实现中,您可以检查用户代理字符串以识别关联的UIWebView,并使用实际的用户代理字符串将其传递给真实的协议处理程序(因此服务器将看不到任何内容)不同)。

所有这些都取决于真正以不同UA字符串结尾的视图。如果你设法让它发挥作用,请告诉我!

答案 1 :(得分:1)

你说你不能把它放在NSURLRequest上,但我不清楚为什么你的最新讨论。这将是最自然的地方。

  • 实施webView:shouldLoadWithRequest:navigationType:
  • 使用objc_setAssociatedObject将额外的属性附加到提供的请求。然后返回YES。 (在这里使用setProperty:forKey:inRequest:会很高兴,但是UIWebView向我们传递了一个不可变的请求,所以我们只能附加相关的对象。另一种方式UIWebView是一个苍白的阴影OS X的WebView,可以处理这个问题。)
  • NSProtocol中,使用objc_getAssociatedObject阅读您的额外媒体资源。请求应与您之前提出的请求相同。你建议事实并非如此。您是说webView:shouldLoadWithRequest:navigationType:的请求与initWithRequest:cachedResponse:client:的请求不同?

我错过了另一个要求或怪癖吗?

答案 2 :(得分:1)

您可以通过自定义请求标头传递选项,假设目标网站或服务提供商不会以某种方式剥离传输中的选项。

可能会出现一种编码方案的挑战,该方案可以合理地编码为头字段值的ASCII字符串,然后解码为您想要的实际值。为此,自定义NSValueTransformer似乎最合适。

答案 3 :(得分:0)

我遇到了同样的问题。我终于坚持马修建议的解决方案(使用用户代理字符串)。但是,由于解决方案没有充实,我添加了一个更详细的新答案。此外,我发现您不需要发送请求以使用户代理“坚持”。只需按照建议here通过javascript即可。

以下步骤对我有用:

(1)获取当前的默认用户代理。稍后您需要将它重新放回NSURLProtocol中的请求中。您需要使用新的webview insatnce,因为获取用户代理将使其坚持Webview,因此您以后无法对其进行更改。

UIWebView* myWebview = [[UIWebView alloc] init];
NSString* defaultUserAgent = [myWebview stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
[myWebview release]; // no needed with ARC, but to emphasize, that the webview instance is not needed anymore

(2)更改standardUserDefaults中的值(取自here)。

NSDictionary* userAgentDict = [NSDictionary dictionaryWithObjectsAndKeys:@"yourUserAgent", @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:userAgentDict];

(3)通过javascript获取新的用户代理字符串,如(1)中所做,但这次是在实际使用的webview实例上。

(4)将standardUserDefaults中的默认用户代理恢复为here