Day17-缓存策略 + URLProtocol 拦截
一、iOS 缓存策略(NSURLRequest.CachePolicy)
缓存策略决定了 请求是否使用缓存,以及如何使用缓存。它在 NSURLRequest
初始化时设置,对 NSURLSession
和 NSURLConnection
都生效。
常见策略说明:
策略 |
描述 |
.useProtocolCachePolicy |
默认策略,遵循 HTTP 协议头(如 Cache-Control、Expires)决定是否使用缓存。 |
.reloadIgnoringLocalCacheData |
忽略本地缓存,总是从服务器加载数据。 |
.returnCacheDataElseLoad |
如果有缓存使用缓存;否则从网络加载。 |
.returnCacheDataDontLoad |
只使用缓存,没有缓存则请求失败(用于离线模式)。 |
.reloadRevalidatingCacheData |
本地有缓存会尝试跟服务器确认其有效性(用 ETag 、Last-Modified 等头)。 |
二、URLProtocol 拦截机制(URLProtocol 子类)
URLProtocol
是 NSURL 加载系统的低层钩子,允许你拦截并处理系统所有通过 NSURL 系列发出的请求(包括 NSURLSession
,如果配置正确)。
主要用途:
- 请求/响应数据 mock(如单元测试)
- 日志埋点、监控网络请求
- 自定义缓存策略(如写入/读取磁盘缓存)
- 拦截请求添加统一 Header、Token
基本使用步骤:
- 自定义类继承
URLProtocol
- 实现必要方法:
1 2 3 4
| + (BOOL)canInitWithRequest:(NSURLRequest *)request; + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request; - (void)startLoading; - (void)stopLoading;
|
- 注册协议类:
1
| [NSURLProtocol registerClass:[MyCustomProtocol class]];
|
- NSURLSession 特别注意:使用自定义配置注册 URLProtocol
1 2 3
| NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; config.protocolClasses = @[MyCustomProtocol.class]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
三、URLProtocol 与缓存策略结合实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| @implementation MyCustomProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request { if ([NSURLProtocol propertyForKey:@"HandledKey" inRequest:request]) return NO; return YES; }
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; }
- (void)startLoading { NSURLRequest *request = self.request;
NSData *cachedData = [self readCacheForRequest:request]; if (cachedData) { NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"application/json" expectedContentLength:cachedData.length textEncodingName:nil]; [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [self.client URLProtocol:self didLoadData:cachedData]; [self.client URLProtocolDidFinishLoading:self]; return; }
NSMutableURLRequest *newRequest = [request mutableCopy]; [NSURLProtocol setProperty:@YES forKey:@"HandledKey" inRequest:newRequest];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURLSessionDataTask *task = [session dataTaskWithRequest:newRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (data) { [self saveCache:data forRequest:request]; [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; [self.client URLProtocol:self didLoadData:data]; } if (error) { [self.client URLProtocol:self didFailWithError:error]; } else { [self.client URLProtocolDidFinishLoading:self]; } }]; [task resume]; }
- (void)stopLoading { }
@end
|
四、补充建议
- 使用
setProperty:forKey:
防止请求递归处理。
- 可实现“先缓存再刷新”策略:先返回缓存再异步更新。
- 可扩展支持磁盘缓存路径、缓存有效期等功能。