URLLoading System( 网络资源加载)
Foundation框架中提供了资源网络请求部分的基本组件,通过指定-URL(资源路径)和TransformProtocol(传输协议),通过stream流实现数据的传输。常见的传输协议主要有以下几种:
*File Transfer Protocol (ftp://) 文件传输协议:常用作文件传输协议,公司内部和局网内常使用。
*Hypertext Transfer Protocol (http://)超文本传输协议: 常用于浏览器中网络请求
*Hypertext Transfer Protocol with encryption (https://)加密的超文本传输协议,就是在http的基础上增加了C/S(服务器与客户端)之间的认证,
Local file URLs (file:///),本地资源传输协议,按照协议规定的格式从本地加载资源,其后指定的就是资源在本地的路径,大家有没有主意到这里有三条斜杠,因为所有的协议必须以 ://+ 主机地址或域名:端口号 +/文件资源路径或者是查询语句。 此处是加载本地文件,所以中间的主机域名和端口号省略了就变成:///。
Data URLs (data://) 数据传输协议。
当然除此之外你还可以定义自己的协议,比如苹果的 sms:// 短信协议, prompt:// ,
- 例如当我门要与HTML5做交互的时候,可以通过定义我门自己的协议头 ourselfprotocol://+你需要拦截信息。 不熟悉的朋友请看UIWebView头文件的代理方法。 通过截获协议头的scheme,我们就能快速的定位那些是需要我门拦截分析的URL了,当然你也可以通过字符串进行分割。
+URL加载系统包括类加载URL以及一些重要的助手类,使用这些URL加载类修改他们的行为。助手类主要分为五类:协议支持,认证和证书,cookie存储、配置管理和缓存管理。Foundation中主要提供了如下几个类用于网络资源的加载.包括目前的很多主流框架如SDWebImage,AFNetWroking,都依赖这些类。主要常用的还是NSURLConnection,NSURLSession,NSURL,NSURLRequest.
资源加载协议类NSURLProtocol
,
*从名字上很容易想到它是一个定义资源加载的协议,其实不然. 它属于一个抽象的类,定义了一些基本的方法,主要是机遇网络请求资源加载的。通常使用它的子类进行扩展,当使用NSURLSession和NSURLConnection进行网络资源加载的时候,通常会去访问NSURLClinetProtocol,即询问请求方是否能够处理这个请求,如果返回能则使用我门自定的子类进行网络资源加载,如果不能则使用系统默认的方式进行资源加载
//这是一个NSURLProtoclClient的对象,它就像一个钩子 一样,如果我们如果在NSURLLoadingSystem询问NSURlProtocol 的canInitWithRequest 方法时候,回答YES,则需要我们自己定义类去处理网络请求,这个时候NSURLProtocolClient实例就是专门来帮我们处理网络请求资源的回调的。之所以系统定义到这个类的属性里,是为了我门在创建NSURLProtocol子类的时候,通过子类能够狠方便的调用 NSURLProtocolClient的实例去处理网路资源的加载。
@property (nullable, readonly, retain) idclient;
//一个请求对象的实例对象,将我门访问的URL资源路径,请求头中的一些参数进行了统一的封装,如 accept type,Content-Type,文件的编码格式,压缩方式,是否保持心跳连接,是否适用Cookie,lastModify time,服务器名称,版本号,协议版本号,请求方式GET,POST,请求资源的长度,常用的就只有accetpt type,Content-Type,
@property (readonly, copy) NSURLRequest *request;
//这个属性从字面意思都能看得出来,就是缓存request 的响应数据的,方便下次系统调用的时候直接获取
@property (nullable, readonly, copy) NSCachedURLResponse *cachedResponse;
//这个属性用到的比较多,系统在请求网络资源或从定向的时候都会来询问这个方法,如果回答是,则表示 使用用户自定义的protocol来处理网络资源的加载,如果我门需要使用离线加载,则需要将此方法进行改写。
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
//这个方法相当与事给我门需要进行请求的Request对象的headerFields进行重新设置,我门可以自定一请求头,这样我门可以通过request headerFields中的某些特定的属性来过滤我门不需要处理的Request,让她们直接采用NSURLLoadingSystem系统的方式进行网络资源的加载。我门可以通过改写系统这个方法,设置它的通用的请求头, 如 appKey :xxxx, userAgent: xxxx,version: ios Mobile
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
//缓存专用API,判断本地缓存中NSURLRequest对象和我门需要请求的NSURLRequest对象是否相同,如果是相通的 。。。那么。。。。
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
//开始进行资源加载,这个方法执行的前提是当我门实现了
- (void)startLoading;
//停止加载
- (void)stopLoading;
+(BOOL)CanInitWithRequet
方法并返回YES后,并且使用+ (BOOL)registerClass:(Class)protocolClass
注册我门自定义的NSURLProtocol子类;才会执行此方法,这就是前面说到的我门自己实现网络资源的加载。
//下面几个APi主要是对我门需要处理的NSURLRequest对象进行处理,标记那些已经是请求过的,那些是没有请求过的。通过KVO的方式给它的headerFIelders添加一个参数进行判定。
+ (nullable id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;
+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
+ (void)removePropertyForKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;
+ (BOOL)registerClass:(Class)protocolClass;
主要是为了配合我门在完成自定义的资源加载过程中的请求,我门只需要在实现的NSURLSession和NSURLConnection的代理方法中通过钩子来实现这些方法就行了,将 URL资源加载成功,加载失败,加载完成,重定向进行重。这样就保证的在我门自定义处理网络资源加载后,最后又将结果移交给了系统URLLoadingSystem处理。
NSURLProtocol子类的实例->URLLoadingSystem-》询问NSURLProtocol canInitWithRequest-(是)》startLoading->调用NSURLSession/NSURLConnection进行网络资源请求-》在请求完成的代理方法中实现NSURLClientProtocol请求处理。-》注销NSURLProtocol子类实例。总的来说系统自动进行URL资源加载->切换手动资源加载-》加载结束切换系统进行URL资源加载。而在手动进行资源加载过程中,这期间的request,response,redirectRequest,cache,data,error,所有的这些时间都由我门自己定义。 你只需要在子类重写的canInitWithRequest方法中进行拦截我门需要处理的请求进行处理就可以了,甚至还可以对我门项目需要离线加载的页面做一个专门的配置文件表,在此方法类的每次请求的URLRequest对象做一个判定,然后再进行处理。
//下面为NSURLClient的实现方法,
- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;
```objective-c
//截获重定向的URL请求头,确定是否响应。
- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;
//缓存是否有效
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;
//设置Request的缓存策略,由于我门是自定义子类,如果你需要完成自定义的离线缓存,那么此方法类需要适用设置为NSURLCacheStoreagePolicy为NO。
- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;
//此方法与NSURLSession didReceiveData 或者NSURLConnection didReceviveData一起使用,将接收到的数据返回给NSURLLoadingSystem(系统资源加载系统).这样才不会影响我门在其他的类文件里调用网络请求方法获取数据。
- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;
- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
*自己对请求的授权挑战进行处理, 因为我门自定义子类,主要是做离线缓存,或者设定Request请求头的一些规则,以及重定向之类的. 此处你也可以为指定的一些https的请求做一些特殊的处理。
*首先需要拿到challenge的保护空间,获取到服务器的受信任对象,serverTrust,其实就是一个证书凭证,不过现在通过stream流传输过来被系统封装成了一对象。 如果这个对象是
- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;