定制实现NSURLProtocol

最近周末出门比较多,好久没写技术日志了,最终blog边栏上还真是少了2015.3,赶忙趁着清明节放假补上一篇。这一篇我们就来看看我们自己可以定制实现的NSURLProtocol

对于一个移动互联网时代的客户端App来说,网络通信请求基本上是不可缺少的。做过很久iOS开发的朋友们可能都记得ASIHTTPRequest,这是一个经典的http网络请求开源工具。不过遗憾的是,这个开源的请求工具目前已经停止维护。而iOS SDK自身在Foundation里也提供了一整套的URL Loading API。即使是广泛被使用的AFNetworking也是基于这套API。

说起Foundation里构建的的URL Loading System,相信大家对NSURL、NSURLRequest、NSURLConnection都不陌生,iOS7之后又增加了NSURLSession。而我们今天要提到的主角则是潜藏在这些之下的一个非常强大的类,NSURLProtocol。

0. 初识NSURLProtocol

NSURLProtocol也是随着最初的URL Loading System推出的,自iOS SDK的2.0版本开始就出现了。然而,由于苹果官方文档中对其介绍的内容非常少非常简短,并没有受到官方关注。但NSURLProtocol可以被当做是整个URL Loading System中功能最强大的API。

NSURLProtocol本身是个“抽象”类(Objective-C语法上没有真正抽象类的概念),除此之外它也是对互联网通信协议(真正的Protocol)的抽象。也就是说,如果实现了它的子类,你就可以写一套和HTTP类似的协议。

具体一些,在iOS App中使用NSURLProtocol:

  • 如果需要,可以对html页面中的图片做本地化处理
  • Mock假的response
  • 对请求头做规范化处理
  • 在上层应用不感知情况下,实现一套代理机制
  • 过滤请求、响应中敏感信息
  • 对已有协议做改进、补充处理

以上这些,说简单点,就是对上层的URLRequest请求做拦截,并根据自己的需求场景做定制化响应处理。

1. 初始化请求

不是所有请求都要经过定制的协议来走,我们自己定制实现的Protocol可以根据不同条件筛选请求,同时对请求做更完整的包装定义。

在NSURLProtocol.h的interface定义中,我们可以看到比较靠上的两个方法分别是:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;

使用canInitWithRequest:我们可以筛选出可以使用当前协议的request,其它的忽略掉,直接走其它协议或者默认实现。

而canonicalRequestForRequest:则是给了我们包装实现,给出完整版request的机会。

2. 属性设置

很多情况下,具体的通信实现最终可能还是要采用系统自身提供的默认实现,但却又需要我们做过滤包装。通常的办法是我们对request按需求场景做特定处理,最后返给系统,走默认实现。

但这样这个request很有可能会再次进入这个protocol,所以通常在对request进行完第一次处理后,打个标,下次进入canInitWithRequest时直接过滤掉。

所谓的打标就会涉及到对request的属性操作,需要如下方法:

+ (id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;
+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
+ (void)removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;

大家可能会想,这样的方法不应该是NSURLRequest类的么?或者是Category?

是的,看起来是可以这样的。但大家注意到这是NSURLProtocol的类(+)方法,也就是说这里的API是以工具方法提供的。

3. 通信协议内容实现和与client交互

下面说实现协议最重要的部分,做一个Protocol总要有实现的。我们的定制实现就要override如下两个方法,并且给出具体的通信逻辑实现。比如你想实现SPDY,那就自己把协议的实现逻辑给出,或者使用三方库协助实现。

- (void)startLoading;
- (void)stopLoading;

在实现这两个方法时,通过URL Loading System和上层打交道是一定要有的。这就是NSURLProtocol.h中的另一部分内容——NSURLProtocolClient。NSURLProtocolClient是一个Objective-C的protocol,定义了一些方法。我们在实现startLoading/stopLoading时,我们只要在有必要和系统交互时拿到self.client对象调用就好,client会由上下文实例化好供我们使用。

4. 协议的注册

上面说了这么多,如果没有这一步都是白扯。最后的一步就是,把实现的协议注册到系统中。这样才算完美,一切都OK了。

按照苹果的Guide文档所写,在处理请求时,会按照NSURLProtocol注册的反序筛选。

5. 其它

以上,NSURLProtocol介绍完了。看,是不是Loading System中最强大的部分?

虽然目前Google已经声称支持HTTP2.0,放弃SPDY,但SPDY依然被广泛使用。SPDY的实现就可以通过NSURLProtocol来简单实现。当然拦截WebView的Request之类的,也可以找NSURLProtocol。

相关文章:

此条目发表在 iOS, iOS开发基础, 开发, 计算机技术 分类目录,贴了 , , , 标签。将固定链接加入收藏夹。
  1. ??????老公:“老婆,晚上吃什么?”老公:“老婆,我们晚上吃什么?”老婆:“有红烧牛肉,海鲜大虾,葱香排骨,泡椒凤爪,黑胡椒牛肉。”老公:“这么多好吃呀,老婆你真好。”老婆拿出几包方便面问道:“这么多口味方便面你要哪种的?”?????? http://url.cn/XfB45g

  2. 那位冒充作者的网友你好,请你不要随便说好吗?就算作者真的不算更新了也不会在这里公布,就算会公布也好,都是在腾讯漫画公布,所以请你不要冒充作者。这是未经过作者本人的同意就滥用本人的名字,会受到法律追究的!