前言
最近公司有一个需求,要对webView(UIWebView)实现缓存机制。即在无网条件下,打开webView页面,能读取到网页,有网情况下,缓存未过期也可以使用本地缓存,加快用户读取网页速度。
实现缓存策略的方案有很多,为了保证有效,可控等多方面因素,本文采用NSURLProtocol来实现该需求,它的优势很多,楼主就不再累述了。
关于NSURLProtocol,网上给出了很多资料,但很多方案都有缺陷,包括github上有star的项目,会遇到在特定情况下,网页加载不出来的问题,导致一直显示空白页。本文成功解决了这些问题,目前该项目已在线上稳定运行。
写这篇文章,一方面为了自己,做一些整理,另一方面如果小伙伴,遇到类似需求后,不至于走太多弯路,所谓前人栽树,后人乘凉。废话不多说了,直接上内容。
首先关于URL Loading System
简单来说,URL Loading System是iOS一系列网络请求类的集合,包括老的NSConnection和现在流行的NSURLSession,还包括一些请求认证的类,一个sessionConfig的类,还有关于处理请求缓存的类等,当然也包括NSURLProtocol类。
NSURLProtocol
当我们需要拦截URL请求时,我们只要通过 - registerClass: 方法注册我们的NSURLProtocol类,然后去重写NSURLProtocol类中的方法,就能对我们发起的请求做处理。
具体使用方法:
1. 继承于NSURLProtocol
NSURLProtocol 是一个抽象类,所以使用的时候必须定义一个它的子类:
2. 注册
需要拦截URL的控制器中注册该类:
记得在不需要的时候,及时关闭它:
注意一点:如果是基于 NSURLSession 进行的请求,注册的时候需要注册到 NSURLSessionConfiguration 中:
3.子类必须实现的方法
注册成功之后,需要子类CLURLSessionProtocol去实现抽象方法:
+ (BOOL)canInitWithRequest:(NSURLRequest*)request
此处可以拦截需要处理的URL,已经处理过的请求,需要标记请求是否被处理过,防止死循环
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
这个方法用来统一处理请求 request 对象的,可以修改头信息,或者重定向。没有特殊需要,则直接return request;
如果要在这里做重定向以及头信息的时候注意检查是否已经添加,因为这个方法可能被调用多次,也可以在后面的方法中做。
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)a toRequest:(NSURLRequest*)b
判断网络请求是否一致,一致的话使用缓存数据。没需要就调用 super 的方法。
- (void)startLoading
这个方法作用很大,把当前请求的request拦截下来以后,在这个方法里面对这个request做各种处理,比如添加请求头,重定向网络,使用自定义的缓存等。
重点:需要标记已经处理过的 request:
- (void)stopLoading
取消流程
4.整理下业务需求:
流程图: