大多数App内部都会采用UIWebView加载的,虽然这样减轻了客户端的工作量,但是,不可避免会遇到网络差,服务器不稳定等的情况,在这种情况下,webView的加载效果就很差了,严重影响客户体验。下面介绍一个NSURLProtocol的缓存的思路
一、原理
主要是通过继承NSURLProtocol,自定义一个子类,然后可以轻松的重新定义苹果的URL加载系统 (URL Loading System)的行为。通过拦截,可以实现各种操作 。
二、实现的主要步骤
首先,要在AppDelegate.m的
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
里面进行注册
[NSURLProtocol registerClass:[MyConnectionURLProtocol class]];
然后,重写NSURLProtocol的方法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
每个请求都会进入这个方法,返回YES表示接管系统请求,自己进行相关操作,返回NO,表示,默认系统的请求。我们可以在这个方法里面判断自己需要做处理的连接,可以根据实现webView缓存,判断要加载的资源在本地是否存在,如果存在,接管系统请求,加载本地资源
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
/*
防止无限循环,因为一个请求在被拦截处理过程中,也会发起一个请求,这样又会走到这里,如果不进行处理,就会造成无限循环
*/
if ([NSURLProtocol propertyForKey:protocolKey inRequest:request]) {
return NO;
}
NSString * url = request.URL.absoluteString;
// 如果url已http或https,开头,则进行拦截处理,否则不处理
if ([url hasPrefix:@"http"] || [url hasPrefix:@"https"]) {
//判断要加载的资源本地是否存在
if ([MyConnectionURLProtocol localResourceIsExistWith:request]) {
return YES;
} else {
return NO;
}
}
return NO;
}
这是相应的判断。
当上面的操作返回为YES的时候,才会继续下面的操作。
- (void)startLoading重写该方法,需要在该方法中发起一个请求,对于NSURLConnection来说,就是创建一个NSURLConnection,对于NSURLSession,就是发起一个NSURLSessionTask,在这里可以进行相关的操作,加载本地资源
NSMutableURLRequest * request = [self.request mutableCopy];
// 表示该请求已经被处理,防止无限循环
[NSURLProtocol setProperty:@(YES) forKey:protocolKey inRequest:request];
NSLog(@"URL %@",request.URL.absoluteString);
NSString *str = request.URL.path;
NSString *Regex = nil;
if ([str.pathExtension isEqualToString:@"js"])
{
Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"js"];
}else if ([str.pathExtension isEqualToString:@"css"])
{
Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"css"];
}else if ([str.pathExtension isEqualToString:@"jpg"]||[str.pathExtension isEqualToString:@"png"])
{
Regex = [MyConnectionURLProtocol addLocalFileReturnRex:@"img"];
}
[MyConnectionURLProtocol rexString:str rex:Regex success:^(NSString *matchStr) {
NSArray *fileArr = [matchStr componentsSeparatedByString:@"."];
[self addFiledToClient:matchStr MIMEType:fileArr.lastObject];
} faild:^{
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
}];
重写- (void)stopLoading该方法,需要停止响应的请求
- (void)stopLoading {
[self.connection cancel];
}
此外,还要重写canonicalRequestForRequest方法,可以增加或修改请求头等操作,主要是改变request的操作
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
// 修改了请求的头部信息
NSMutableURLRequest *mutableReqeust = [request mutableCopy];
mutableReqeust = [self redirectHostInRequset:mutableReqeust];
return mutableReqeust;
}
三、实现NSURLConnectionDataDelegate协议代理方
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
对于数据更新要求不是很及时的,可以采用以上缓存方案。