NSURLProtocol
在 Apple 的文档 URL Loading System
中,详细介绍了一个请求是如何创建、发起、回传数据的,还包括了证书认证,cookie 等一系列的操作,但是好像唯独遗漏了一个特殊的类: NSURLProtocol
,其实在我们日常开发中,是有可能需要跟这个类打交道的,比如网页的缓存,request 重定向等一系列的需求。
关于 Apple 对 NSURLProtocol 的介绍
每次发起请求时,系统会寻找或者创建一个合适的 protocol
对象来响应 request
,而在 protocol
中,有一系列的方法会被调用,用来确定是否能够响应这次请求,并且允许通过返回参数来替换原本的请求。NSURLProtocol
是一个抽象类,里面定义许多发起请求时,系统自动回调的方法。我们需要创建一个继承自 NSURLProtocol
的子类,并且实现某些方法,并且将类注册到系统中。系统发起请求时,按照注册的顺序,倒序询问所有的 protocol,假如能处理这次请求的话,就将处理转到这个类中。
创建一个 Protocol 对象
- initWithRequest:cachedResponse:client:
- initWithTask:cachedResponse:client:
当我们想在初始化的时候,自定义一些操作的话,我们可以重写上面这俩个方法,但是我们不需要手动调用初始化方法生成对象,这两个方法是系统会自动调用的。
向系统中注册 Protocol 类
+ (BOOL)registerClass:(Class)protocolClass
向系统中注册我们自定义的 protocol
,只有这个类不是 NSURLProtocol
子类时,这个方法才会返回 NO。当系统发起一次请求时,会按照注册顺序,倒序的调用子类的方法:canInitWithRequest
,第一个返回 YES 的类,会被确定为这次响应的 protocol。
Important
需要注意的是,当我们用NSURLProtocol
发起请求时,注册方法应该如下:
1 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; |
需要将自定义的 protocol 注册到 session 的 configuration.protocolClasses 中,才能起作用。
+ (void)unregisterClass:(Class)protocolClass
这个方法调用后,该类不再被系统询问。
决定一个 NSURLProtocol 子类是否支持响应某个 request
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
一个子类必须实现这个方法,来告诉系统,是否由该子类响应这次请求,返回 YES 的话,系统创建该类的对象,并且后续的类方法和对象方法会继续被回调,,返回 NO 的话,系统越过该子类,继续查找。
+ (BOOL)canInitWithTask
使用 NSURLSession
发起请求时,会先调用这个方法,yes 的话,继续调用上面那个方法。功能与上线方法类似。
提供给系统一个标准化的 request
假如一个子类可以响应一个请求,系统会继续调用下面的方法:
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
在这个方法中,我们可以返回一个自定义的 request,系统会替换原有的,也可以直接返回原有的 request。
开始和停止请求
- (void)startLoading
当这个方法被调用时,子类必须发起请求,并通过 client ,将数据回传给系统,系统再将数据传递给本来的监听者。
- (void)stopLoading
这个方法被调用时,子类需要停止自己的请求。
总结
这个类是比较简单的一个类,可以当成是,苹果允许的一个中间人攻击,当发起一个请求时,我们可以通过注册 protocol ,替换这次请求,发起我们自己的请求,并通过 client ,将数据传给系统,这一切对 urlSessionDelegate 来说都是透明的。
简单的数据流通图