一、说明:

NSURLProtocol可以拦截任何网络请求,包含UIWebView中发出的所有请求。但是在WKWebView中,只能拦截到最初始的请求,内嵌的资源下载拦截不到。比如通过WKWebView加载"http://www.baidu.com",则只能拦截到"http://www.baidu.com",网页内部的资源加载拦截不到。页面跳转属于最初始请求之内,可以拦截到。


二、创建NSURLProtocol的子类,通过下面的代码注册此协议类:

[NSURLProtocol registerClass:[MyURLProtocol class]];

三、下面是此子类的代码:


#import "MyURLProtocol.h"

#define MyURLProtocolHandled @"MyURLProtocolHandled"

//创建archive数据模型,重写编码解码协议

@interface MyCacheData : NSObject

@property(nonatomic,strong) NSURLRequest *request;

@property(nonatomic,strong) NSURLResponse *response;

@property(nonatomic,strong) NSData *data;

@end

@interface NSURLRequest (MutableCopyWorkaround)

- (id)mutableCopyWorkaround;

@end

@interface MyURLProtocol ()

@property(nonatomic,strong) NSURLConnection *connection;

@property(nonatomic,strong) NSMutableData *httpData;

@property(nonatomic,strong) NSURLResponse *response;

@end

@implementation MyURLProtocol

#pragma mark - 重写NSURLProtocol子类方法

+ (BOOL)canInitWithRequest:(NSURLRequest *)request

{

//如果此请求是拦截到请求之后,接管请求而发起的新请求,则不处理。

if ([request.URL.scheme isEqualToString:@"http"] &&

[request valueForHTTPHeaderField:MyURLProtocolHandled] == nil)

{

return YES;

}

return NO;

}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request

{

return request;

}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a

toRequest:(NSURLRequest *)b

{

return [super requestIsCacheEquivalent:a toRequest:b];

}

- (void)startLoading

{

//如果发现已经存在此请求的缓存数据,则返回缓存数据,否则发起新的请求从服务求加载数据

MyCacheData *cacheData = [NSKeyedUnarchiver unarchiveObjectWithFile:

[self cachePathForRequest:self.request]];

if(cacheData != nil)

{

NSData *data = cacheData.data;

NSURLResponse *response = cacheData.response;

NSURLRequest *redirectRequest = cacheData.request;

//使用NSURLProtocolClient做请求转向,直接将请求和数据转发到之前的请求

if(redirectRequest != nil)

{

[[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest

redirectResponse:response];

}

else

{

[[self client] URLProtocol:self didReceiveResponse:response

cacheStoragePolicy:NSURLCacheStorageNotAllowed];

[[self client] URLProtocol:self didLoadData:data];

[[self client] URLProtocolDidFinishLoading:self];

}

}

else

{

//接管此网络请求,发起一个新的请求,后续会将新请求拿到的数据交给之前的旧请求

NSMutableURLRequest *connectionRequest = [[self request] mutableCopyWorkaround];

//增加标记,标示是由我们接管而发出的请求

[connectionRequest setValue:@"Tag" forHTTPHeaderField:MyURLProtocolHandled];

self.connection = [NSURLConnection connectionWithRequest:connectionRequest

delegate:self];

}

}

- (void)stopLoading

{

[self.connection cancel];

self.connection = nil;

}

#pragma mark - 网络请求代理

- (NSURLRequest *)connection:(NSURLConnection *)connection

willSendRequest:(NSURLRequest *)request

redirectResponse:(NSURLResponse *)response

{

if(response != nil)

{

NSMutableURLRequest *redirectableRequest = [request mutableCopyWorkaround];

//缓存数据

MyCacheData *cacheData = [MyCacheData new];

[cacheData setData:self.httpData];

[cacheData setResponse:response];

[cacheData setRequest:redirectableRequest];

[NSKeyedArchiver archiveRootObject:cacheData

toFile:[self cachePathForRequest:[self request]]];

//将请求和缓存的响应数据转向到之前的请求

[[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest

redirectResponse:response];

return redirectableRequest ;

}

return request;

}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection

{

return YES;

}

- (void)connection:(NSURLConnection *)connection

didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

{

[self.client URLProtocol:self didReceiveAuthenticationChallenge:challenge];

}

- (void)connection:(NSURLConnection *)connection

didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

{

[self.client URLProtocol:self didCancelAuthenticationChallenge:challenge];

}

- (void)connection:(NSURLConnection *)connection

didReceiveResponse:(NSURLResponse *)response

{

//保存响应对象

self.response = response;

[self.client URLProtocol:self didReceiveResponse:response

cacheStoragePolicy:NSURLCacheStorageNotAllowed];

}

- (void)connection:(NSURLConnection *)connection

didReceiveData:(NSData *)data

{

[self.client URLProtocol:self didLoadData:data];

//保存服务器返回的数据

if(self.httpData == nil) {

self.httpData = [NSMutableData dataWithData: data];

}

else

{

[self.httpData appendData:data];

}

}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection

willCacheResponse:(NSCachedURLResponse *)cachedResponse

{

return cachedResponse;

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

{

[self.client URLProtocolDidFinishLoading:self];

//请求加载完毕之后,将数据缓存

MyCacheData *cacheData = [MyCacheData new];

[cacheData setData:self.httpData];

[cacheData setResponse:self.response];

[NSKeyedArchiver archiveRootObject:cacheData

toFile:[self cachePathForRequest:self.request]];

self.connection = nil;

self.httpData = nil;

self.response = nil;

}

- (void)connection:(NSURLConnection *)connection

didFailWithError:(NSError *)error

{

[self.client URLProtocol:self didFailWithError:error];

self.connection = nil;

self.httpData = nil;

self.response = nil;

}

#pragma mark - 为请求创建缓存路径

- (NSString *)cachePathForRequest:(NSURLRequest *)aRequest

{

NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,

NSUserDomainMask, YES) lastObject];

return [cachesPath stringByAppendingPathComponent:

[NSString stringWithFormat:@"%ld", [[[aRequest URL] absoluteString] hash]]];

}

@end

@implementation NSURLRequest (MutableCopyWorkaround)

- (id) mutableCopyWorkaround {

NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequest alloc]

initWithURL:[self URL]

cachePolicy:[self cachePolicy]

timeoutInterval:[self timeoutInterval]];

[mutableURLRequest setAllHTTPHeaderFields:[self allHTTPHeaderFields]];

return mutableURLRequest;

}

@end

@implementation MyCacheData

-(id) initWithCoder:(NSCoder *) aDecoder

{

self = [super init];

if(!self) {

return nil;

}

[self setData:[aDecoder decodeObjectForKey:@"data"]];

[self setRequest:[aDecoder decodeObjectForKey:@"request"]];

[self setResponse:[aDecoder decodeObjectForKey:@"response"]];

return self;

}

- (void)encodeWithCoder:(NSCoder *)aCoder

{

[aCoder encodeObject:[self data] forKey:@"data"];

[aCoder encodeObject:[self request] forKey:@"request"];

[aCoder encodeObject:[self response] forKey:@"response"];

}

@end

部分代码转自:http://tanlimin201.blog.163.com/blog/static/38171407201383032914736/

使用NSURLProtocol实现离线缓存相关推荐

  1. 使用NSURLProtocol实现UIWebView的离线缓存的简单实现

    文章介绍了使用NSURLProtocol实现UIWebView的离线缓存的简单实现,你可以在github上下载这个demo的代码. 无论是"MKNetworkKit"还是" ...

  2. 离线缓存之RNCachingURLProtocol解析

    主要功能:实现 网页离线加载.基本思想来源于AFCache.作者是Rob Napier(IOSX Programming的作者). 使用方法: To build, you will need the ...

  3. 源码推荐:移动端商城(微信小程序源代码) WebView离线缓存

    移动端商城(微信小程序源代码)(上传者:腾讯攻城师jack) 功能包括:商品橱窗,商品搜索,购物车,结账等功能. TableView嵌套webView自适应高度(上传者:linlinchen) tab ...

  4. 关于域用户的离线缓存登录知识分享

    关于域用户的离线缓存登录知识分享 微软设计缓存登录的目的? 缓存登录主要是为了解决当公司域控制器发生故障联系不上DC或用户拿笔记本电脑回家不拔×××的情况下,依然能够登录到系统,进行办公.如果用户登录 ...

  5. 离线缓存占内存吗_彻底弄懂浏览器缓存策略

    浏览器缓存策略对于前端开发同学来说不陌生,大家都有一定的了解,但如果没有系统的归纳总结,可能三言两语很难说明白,甚至说错,尤其在面试过程中感触颇深,很多候选人对这类基础知识竟然都是一知半解,说出几个概 ...

  6. html开启页面离线缓存,HTML5 离线缓存

    离线缓存 applicationCache 第一次加载后将数据缓存,如果没有清除缓存,下一次没有网络也能加载. 使用 1. 使用 manifest 属性,引入 .appcache 文件 每个指定了 m ...

  7. html5 客户端数据缓存机制,深入理解HTML5离线缓存机制

    TML5提供了一种离线应用缓存机制,使得网页应用可以离线使用,这种机制在移动端浏览器上支持度非常广,所有版本的android和ios浏览器都能很好的支持.我们可以放心的使用该特性来加速移动端页面的访问 ...

  8. javascript中本地储存、离线缓存、地理定位、网络状态

    本地储存: 实际开发中某些内容是不需要放到服务器中,而是放到了浏览器中,需要的时候可以快速的访问,甚至页面刷新也可能不会丢失数据,容量较大:这里介绍两种数据存储方式:sessionStorage约5M ...

  9. 使用 jQuery Mobile 与 HTML5 开发 Web App (十七) —— HTML5 离线缓存

    本文要介绍的,是 HTML5 离线网络应用程序的特性,离线网络应用程序在 W3C 中的实际名称是 "Offline Web applications" ,也称离线缓存.当用户打开浏 ...

最新文章

  1. centos7 systemctl 管理 mysql
  2. Silverlight for Windows Phone 7开发系列(2):第一个Silverlight程序
  3. Visual Studio 2008 当页面进行调试时,IE8浏览器显示的是空白页面?
  4. java和tornado_TornadoJ
  5. 那是两个小时我不会回来
  6. python.day05
  7. BZOJ 1270: [BeijingWc2008]雷涛的小猫( dp )
  8. 如何使用git命令行上传项目到github
  9. Problem C: 01字串
  10. 【线性代数本质】1:向量究竟是什么
  11. Java Persistence with MyBatis 3(中国版)
  12. Tuxedo 8.110gR3 开发环境的安装与配置
  13. 库、教程、论文实现,这是一份超全的PyTorch资源列表(Github 2.2K星)
  14. 开启Mac原生NTFS支持
  15. DarkAngels勒索病毒分析
  16. python3的字符串操作
  17. mysql分页查询如何优化_mysql分页查询优化
  18. python 实现改变excel文件列宽
  19. mysql 数据表格切分_MySQL数据库垂直和水平切分
  20. 手把手教你如何在AWS EC2 启用 IPv6

热门文章

  1. LeetCode 206 Reverse Linked List 解题报告
  2. sql注入基于错误-单引号-字符型
  3. 模拟 Codeforces Round #288 (Div. 2) A. Pasha and Pixels
  4. [LeetCode] Plus One
  5. Innodb 表空间卸载、迁移、装载
  6. Ubuntu桌面培训(Ubuntu Desktop Course)中文译本发布
  7. linux网络编程——boa移植
  8. CentOS7.7安装MySQL5.6并配置环境变量(详细版)
  9. 如何获取filecoin_获得Filecoin币有哪些条件?
  10. ios笔试题算法_微软笔试题-Dijkstra算法