NSURLProtocol

NSURLProtocol

NSURLProtocol 是 iOS里面的URL Loading System的一部分,URL loading system 原生已经支持了 http,https,file,ftp,data 这些常见协议,当然也允许我们定义自己的 protocol 去扩展,或者定义自己的协议。当URL loading system通过 NSURLRequest 对象进行请求时,将会自动创建 NSURLProtocol (是一个对象)的实例(可以是自定义的),这样我们就有机会对该请求进行处理。

  • 可以拦截 UIWebViewWKWebView (需额外处理),基于系统的 NSURLConnection 或者 NSURLSession 进行封装的网络请求。
  • 忽略网络请求,直接返回自定义的 Response
  • 修改 request (请求地址,认证信息等等)
  • 返回数据拦截

对URL Loading System不清楚的,可以看看下面这张图,看看里面有哪些类:

image.png



使用NSURLProtocol的主要可以分为5个步骤:
注册—>拦截—>转发—>回调—>结束

NSURLProtocol的创建

@interface CFTHTTPProtocol : NSURLProtocol

@end
  • 首先是继承系统的 NSURLProtocol :
[NSURLProtocol registerClass:[CFTHTTPProtocol class]];
  • 然后在 application:didFinishLaunchingWithOptions: 方法中注册,一旦注册完毕后,它就有机会来处理所有交付给URL Loading system的网络请求。

子类NSURLProtocol必须实现的方法

+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
  • 拦截,这个方法是自定义 protocol 的入口,如果不打算处理,返回NO,如果你需要对自己关注的请求进行处理则返回YES,这样,URL loading system将会把本次请求的操作都给了你这个 protocol

    这里有个需要注意的地方,想象一下,当你去加载一个URL资源的时候,URL Loading System会询问 CustomURLProtocol 是否能处理该请求,你返回YES,然后URL Loading System会创建一个 CustomURLProtocol 实例然后调用 NSURLSession 去获取数据,然而这也会调用URL Loading System,而你在 +canInitWithRequest: 中又总是返回YES,这样URL Loading System又会创建一个 CustomURLProtocol 实例导致无限循环。我们应该保证每个request只被处理一次,可以通过 +setProperty:forKey:inRequest: 标示那些已经处理过的request,然后在 +canInitWithRequest: 中查询该request是否已经处理过了,如果是则返回NO。
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request
  • 这个方法主要是用来返回格式化好的 request ,如果自己没有特殊需求的话,直接返回当前的 request 就好了。如果你想做些其他的,比如地址重定向,或者请求头的重新设置,你可以copy下这个 request 然后进行设置并返回一个新的 request ,这是一个抽象方法,子类必须实现。
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
  • 这个方法用于判断你的自定义reqeust是否相同,这里返回默认实现即可。它的主要应用场景是某些直接使用缓存而非再次请求网络的地方。
- (void)startLoading;
- (void)stopLoading;
  • 这两个方法主要是开始和取消相应的request,而且需要标示那些已经处理过的request。

实现NSURLSessionDataDelegate和NSURLSessionTaskDelegate

回调,如果你对你关注的请求进行了拦截,那么你就需要通过实现 NSURLProtocolClient 这个协议的对象将消息转给URL loading system,也就是 NSURLProtocol 中的 client 这个对象。看看这个 NSURLProtocolClient 里面的方法:

- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;

- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

你会发现和 NSURLSessionDelegate 很像,其实就是做了个转发的操作。

你可能感兴趣的:(NSURLProtocol)