方式一:截取请求正则、图片缓存
实现webview缓存网页内容难点在缓存图片上。html代码的缓存相对简单,
具体实现思路是这样的:
第1步、先获取html页面里所有图片地址。
方法一:离线获取获取到html代码。html代码你可以把他理解成是一个很长的字符串。通过正则表达式把这个html页面里的所有img标签url。如果是相对url,就加上host。如果是绝对url,就直接下载。这样这个页面里的所有图片路径都拿到了。
方法一的获取img标签url的正则表达式:
NSString *urlPattern = @"<img[^>]+?src=["']?([^>'"]+)["']?";
方法二:通过webview和js 本地程序的交换,获取到html页面所有图片下载地址。
webview和本地程序交互的方法是_detailWebView stringByEvaluatingJavaScriptFromString。
这是方法二获取图片url的js代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
//获取web里的所有的img url - ( NSString *)createImgArrayJavaScript{ NSString *js = @ "var imgArray = document.getElementsByTagName('img'); var imgstr = ''; function f(){ for(var i = 0; i < imgArray.length; i++){ imgstr += imgArray[i].src;imgstr += ';';} return imgstr; } f();" ; return js; } //返回web img图片的数量 - ( NSString *)createGetImgNumJavaScript{ NSString *js = @ "var imgArray = document.getElementsByTagName('img');function f(){ var num=imgArray.length;return num;} f();" ; return js; } |
第2步、把下载图片到本地,把本地的图片设置到html代码中去显示。
通过上面说的两个方法,你可以获取到图片地址了并且能下载到本地了。那没网络的情况下怎么把这些图片再设置到html页面里呢?
方法:下载到本地的图片命名一律使用图片url的md5。因为url直接做不了图片的名称。
下面这段代码演示了下载图片和设置本地图片的全过程。代码的逻辑是:有本地图片,就显示本地图片,如果没有则从网络获取。还有对应的js代码。设置图片是还判断图片的宽度,大于300时,就等比例缩小。
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
|
- ( void )downLoadImageFromURL:( NSArray * )imageUrlArray { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul); dispatch_group_t group = dispatch_group_create(); for ( int i = 0; i < imageUrlArray.count; i++) { NSString *imageUrl = [imageUrlArray objectAtIndex:i]; NSString *key = [imageUrl MD5Hash]; NSData *data = [FTWCache objectForKey:key]; NSURL *imageURL = [ NSURL URLWithString:imageUrl]; NSString *index = [ NSString stringWithFormat:@ "%d" , i]; if (data) { [_detailWebView stringByEvaluatingJavaScriptFromString:[ self createSetImageUrlJavaScript:index imgUrl:key]]; } else { dispatch_group_async(group, queue, ^{ NSData *data = [ NSData dataWithContentsOfURL:imageURL]; if (data != nil ) { [FTWCache setObject:data forKey:key]; dispatch_sync(dispatch_get_main_queue(), ^{ [_detailWebView stringByEvaluatingJavaScriptFromString:[ self createSetImageUrlJavaScript:index imgUrl:key]]; DDLOG(@ "image i %d" ,i); }); } }); } } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //这里是所有图片下载完成后执行的操作。 }); dispatch_release(group); } //设置下载完成的图片到web img - ( NSString *)createSetImageUrlJavaScript:( NSString *) index imgUrl:( NSString *) url{ NSData *imageData = [FTWCache objectForKey:url]; UIImage *image = [ UIImage imageWithData:imageData]; int imageWidth = 300; int imageHeight = image.size.height*300.0f/image.size.width; NSString *js = [ NSString stringWithFormat:@ "var imgArray = document.getElementsByTagName('img'); imgArray[%@].src="%@"; imgArray[%@].width="%d";imgArray[%@].height="%d" ;" , index, url, index,imageWidth,index,imageHeight]; return js; } |
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
|
- ( void ) getImageUrlArray:( NSString *) content { DDLOG_CURRENT_METHOD; NSString *urlPattern = @ "<img[^>]+?src=["']?([^>'"]+)["']?" ; NSError *error = [ NSError new ]; NSRegularExpression *regex = [ NSRegularExpression regularExpressionWithPattern:urlPattern options: NSRegularExpressionCaseInsensitive error:&error ]; //match 这块内容非常强大 NSUInteger count =[regex numberOfMatchesInString:content options: NSRegularExpressionCaseInsensitive range: NSMakeRange (0, [content length])]; //匹配到的次数 if (count > 0){ NSArray * matches = [regex matchesInString:content options: NSMatchingReportCompletion range: NSMakeRange (0, [content length])]; for ( NSTextCheckingResult *match in matches) { NSInteger count = [match numberOfRanges]; //匹配项 for ( NSInteger index = 0;index < count;index++){ NSRange halfRange = [match rangeAtIndex:index]; if (index == 1) { [listImage addObject:[content substringWithRange:halfRange]]; } } } //遍历后可以看到三个range,1、为整体。2、为([\w-]+\.)匹配到的内容。3、(/?[\w./?%&=-]*)匹配到的内容 } } |
方式二:NSURLProtocol缓存
可能很多人使用过UIWebView,也考虑过如何在离线状态下读取缓存的问题,但是好像没有一个标准的答案。有的人说可以,有的人说不行。通过这几天的研究,我发现了一些问题,在这里希望各位给点意见。
另外,还要求助一下,有没有人知道在ios上测试网页加载速度的工具啊? 找了好久都没看到。最好是免费的。
一.HTML5 , Manifest
最开始我的想法是使用HTML5中的离线存储功能,也就是分析Manifest文件来存储和更新部分资源文件。但是经过实践发现,UIWebView根本不支持HTML5,他只实现了Webkit中页面渲染的那一部分。所以要实现缓存必须要另辟蹊径。
二.ASIHTTPRequest,ASIDownloadCache 和 ASIWebPageRequest
首先我得说,这确实是个很好的框架,使用起来确实很方便,但是对于缓存这个问题,好像也跟第二点提到的效果差不多,加载速度没有明显的提升,离线模式下也无法加载。这是实现的代码
:
- -(void)loadURL:(NSURL*)url
- {
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- //ASIWebPageRequest *request= [ASIWebPageRequest requestWithURL:url];
- [request setDelegate:self];
- //[request setUrlReplacementMode:ASIReplaceExternalResourcesWithData];
- [request setDidFailSelector:@selector(webPageFetchFailed:)];
- [request setDidFinishSelector:@selector(webPageFetchSucceeded:)];
- //设置缓存
- [request setDownloadCache:[ASIDownloadCache sharedCache]];
- //[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
- [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
- [request setDownloadDestinationPath:[[ASIDownloadCache sharedCache]pathToStoreCachedResponseDataForRequest:request]];
- [request startAsynchronous];
- }
- - (void)webPageFetchFailed:(ASIHTTPRequest *)theRequest
- {
- // Obviously you should handle the error properly...
- NSLog(@"%@",[theRequest error]);
- NSString *path = [[NSBundle mainBundle] pathForResource:@"error1.html" ofType:nil inDirectory:@"WebResources/Error"];
- NSURL *url=[NSURL fileURLWithPath:path];
- [viewer loadRequest:[NSURLRequest requestWithURL:url]];
- }
- - (void)webPageFetchSucceeded:(ASIHTTPRequest *)theRequest
- {
- NSString *response = [NSString stringWithContentsOfFile:
- [theRequest downloadDestinationPath] encoding:[theRequest responseEncoding] error:nil];
- // Note we're setting the baseURL to the url of the page we downloaded. This is important!
- [viewer loadHTMLString:response baseURL:[theRequest url]];
- //[viewer loadHTMLString:response baseURL:nil];
- }
尽管在官方的说明文档里面说到NSURLCache和NSCachedURLResponse可以用于缓存,但经我测试好像仅仅只能用于加载本地某些资源文件(这里有一篇博客,原文是英文的,这是翻译过来的)
,而且还有大小的限制(好像根据iphone的版本不同而不同,最小是25KB吧),比如图片和JS代码, 而对于整体的页面无法进行加载。而且经过测试也没有感觉加载速度有明显的提高,我用的缓存策略是NSURLRequestReturnCacheDataElseLoad(可能是没有读取本地的缓存文件?),离线模式下也无法加载(可能是baseURL的关系?)。
这找到一篇博客,一种新的解决思路,经过我测试,可以很好的实现缓存。
另外做一点引申,对于动态获取数据的页面,我们不需要缓存的那些请求,只要过滤掉就可以了。
先新建一个文件,把所有不需要缓存的请求的URL写在一个文件里,就象HTML5的 Cache Manifest那样。
然后需要使用缓存的时候读取这个文件,并在重写的- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request 这个方法内对请求进行判断,如果是属于这个文件内的,比如web service的请求就直接返回,其他的就继续处理。
搜索解决方案的时候找到了Rob Napier 的博文:Drop-in offline caching for UIWebView (and NSURLProtocol) 文章介绍了使用NSURLProtocol实现UIWebView的离线缓存的简单实现,你可以在github上下载这个demo的代码。
rob认为无论是“MKNetworkKit”还是”AFCache”实现的缓存都过于复杂,而他想要的是一个简单机制:
1、你使用了UIWebView指向来显示一个有图像嵌入的网站。
2、当你的设备online时,你有正常的缓存算法。
3、当你的设备offline时,你可以显示页面的最后一个版本。
这个demo里做了一个很简单的测试:将cnn.com运行一次,然后再将网络断掉,去浏览这些数据。
现有解决方案:
Matt Gallagher 有一些有趣的想法,使用NSURLCache的子类来实现,但是Rob发现这是不可靠的,尤其是iOS5的HTTP缓存规则十分复杂,在许多情况下如果你不访问服务器便不能获知你缓存的数据是否有效。另外,一些必要的素材如果没有被缓存,那么在离线时前期做的缓存工作就实效了。(辉:NSURLCache实现离线阅读的一点小心得 我也曾讨论了一些相关问题)
AFCache也被认为是一个很好的解决方案(辉:有时间我会对这个开源库进行详细评估,表面上看就是connection、NSURLCache、NSURLProtocol的综合解决方案)。短时间内作者并没有使测试通过,但是AFCache的作者也在文章后边回复说,采纳了Rob的想法,已经提交代码到github上。
要点:
1、尽早注册你的URLProtocol(application:didFinishLaunchingWithOptions:)。
2、NSURLProtocol是NSURLConnection的handler。NSURLConnection的每个请求都会去便利所有的Protocols,并询问你能处理这个请求么(canInitWithRequest: )。如果这个Protocol返回YES,则第一个返回YES的Protocol会来处理这个connection。Protocols的遍历是反向的,也就是最后注册的Protocol会被优先判断。
3、 当你的handler被选中了,connection就会调用–> initWithRequest:cachedResponse:client:,紧接着会调用–>startLoading。然后你需要负责回调:–>URLProtocol:didReceiveResponse:cacheStoragePolicy:,有些则会调用:–>URLProtocol:didLoadData:, 并且最终会调用–>URLProtocolDidFinishLoading:。你有没有发现这些方法和NSURLConnection
delegate的方法非常类似——这绝非偶然!
4、当online的情况下,RNCachingURLProtocol只是负责将请求转发给一个新的NSURLConnection,并且拷贝一份结果给原来的connection。offline时, RNCachingURLProtocol就会从磁盘里载入先前的结果,并将这些数据发回给连接。整个过程只有区区200行代码(不包含Reachability)。
5、这里还有一个有趣的问题,就是当RNCachingURLProtocol创建了一个新的NSURLConnection的,即新的connection也会去找一个handler。 如果RNCachingURLProtocol说可以处理,那么就死循环了。怎么解决呢?通过添加自定义HTTP Header(X-RNCache)来标记这个请求,告诉RNCachingURLProtocol不要再处理这个请求。
6、它可以响应所有的connection,所以你可能需要修改canInitWithRequest:来
选择你要缓存的数据。
另外:并发请求或复杂网络请求的缓存请使用MKNetworkKit(我们也在一个项目中使用了这个类库,非常轻量快捷是ASI的很不错的替代品)。
总结一下:
这项技术不是用来替代AFCache、MKNetworkKit的,他只是用来解决独立的、简单问题的(当然它也可以通过复杂实现来解决复杂问题)。 NSURLProtocol
是非常强大的,Rob已经使用它来监听网络流量(如PandoraBoy中的几个ProxyURLProtocol类)。它非常值得你将其添加到你的工具箱中。
#方式二还有某技术人员分享的代码如下

//WebCachedData.h #import <Foundation/Foundation.h> static NSUInteger const kCacheExpireTime = 600;//缓存的时间 默认设置为600秒 @interface WebCachedData :NSObject <NSCoding> @property (nonatomic,strong) NSData *data; @property (nonatomic,strong) NSURLResponse *response; @property (nonatomic,strong) NSURLRequest *redirectRequest; @property (nonatomic,strong) NSDate *date; @end //WebCachedData.m #import "WebCachedData.h" static NSString *const kDataKey =@"data"; static NSString *const kResponseKey =@"response"; static NSString *const kRedirectRequestKey =@"redirectRequest"; static NSString *const kDateKey =@"date"; @implementation WebCachedData - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:[self data] forKey:kDataKey]; [aCoder encodeObject:[self response] forKey:kResponseKey]; [aCoder encodeObject:[self redirectRequest] forKey:kRedirectRequestKey]; [aCoder encodeObject:[self date] forKey:kDateKey]; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self !=nil) { [self setData:[aDecoder decodeObjectForKey:kDataKey]]; [self setResponse:[aDecoder decodeObjectForKey:kResponseKey]]; [self setRedirectRequest:[aDecoder decodeObjectForKey:kRedirectRequestKey]]; [self setDate:[aDecoder decodeObjectForKey:kDateKey]]; } return self; } @end //CacheURLProtocol.h #import <Foundation/Foundation.h> #import "WebCachedData.h" @interface CacheURLProtocol : NSURLProtocol @end //CacheURLProtocol.m #import "CacheURLProtocol.h" #import <CommonCrypto/CommonDigest.h> static NSString *const kOurRecursiveRequestFlagProperty =@"COM.WEIMEITC.CACHE"; static NSString *const kSessionQueueName =@"WEIMEITC_SESSIONQUEUENAME"; static NSString *const kSessionDescription =@"WEIMEITC_SESSIONDESCRIPTION"; @interface CacheURLProtocol()<NSURLSessionDataDelegate> @property (nonatomic,strong) NSURLSession *session; @property (nonatomic,copy) NSURLSessionConfiguration *configuration; @property (nonatomic,strong) NSOperationQueue *sessionQueue; @property (nonatomic,strong) NSURLSessionDataTask *task; @property (nonatomic,strong) NSMutableData *data; @property (nonatomic,strong) NSURLResponse *response; - (void)appendData:(NSData *)newData; @end @implementation CacheURLProtocol static NSObject *CacheURLProtocolIgnoreURLsMonitor; static NSArray *CacheURLProtocolIgnoreURLs; + (BOOL)registerProtocolWithIgnoreURLs:(NSArray*)ignores { [self unregisterCacheURLProtocol]; [self setIgnoreURLs:ignores]; return [[self class] registerClass:[self class]]; } + (void)unregisterCacheURLProtocol { [self setIgnoreURLs:nil]; [[self class] unregisterClass:[self class]]; } - (void)dealloc { [self.task cancel]; [self setTask:nil]; [self setSession:nil]; [self setData:nil]; [self setResponse:nil]; [self setSessionQueue:nil]; [self setConfiguration:nil]; [[self class] setIgnoreURLs:nil]; } +(void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ CacheURLProtocolIgnoreURLsMonitor = [NSObject new]; }); } #pragma mark - URLProtocol APIs + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { // 可以修改request对象 return request; } + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b { return [super requestIsCacheEquivalent:a toRequest:b]; } + (BOOL)canInitWithTask:(NSURLSessionTask *)task { return [self canInitWithURLRequest:task.currentRequest]; } - (instancetype)initWithTask:(NSURLSessionTask *)task cachedResponse:(nullable NSCachedURLResponse *)cachedResponse client:(nullable id <NSURLProtocolClient>)client { self = [super initWithTask:task cachedResponse:cachedResponse client:client]; if (self !=nil) { [self configProtocolParam]; } return self; } + (BOOL)canInitWithRequest:(NSURLRequest *)request { return [self canInitWithURLRequest:request]; } - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client { self = [super initWithRequest:request cachedResponse:cachedResponse client:client]; if (self !=nil) { [self configProtocolParam]; } return self; } - (void)startLoading { WebCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]]; // 这地方不能判断cache.data字段,有可能是一个重定向的request if (cache) { // 本地有缓存 NSData *data = [cache data]; NSURLResponse *response = [cache response]; NSURLRequest *redirectRequest = [cache redirectRequest]; NSDate *date = [cache date]; if ([self expireCacheData:date]) { // 数据过期 NSLog(@"request Data-expire!"); NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy]; [[self class] setProperty:@(YES) forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest]; self.task = [self.session dataTaskWithRequest:recursiveRequest]; [self.task resume]; } else { if (redirectRequest) { [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]; } else { if (data) { NSLog(@"cached Data!"); [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [[self client] URLProtocol:self didLoadData:data]; [[self client] URLProtocolDidFinishLoading:self]; } else { // 本地没有缓存上data NSLog(@"request Data-uncached data!"); NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy]; [[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest]; self.task = [self.session dataTaskWithRequest:recursiveRequest]; [self.task resume]; } } } } else { // 本地无缓存 NSLog(@"request Data-no data!"); NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy]; [[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest]; self.task = [self.session dataTaskWithRequest:recursiveRequest]; [self.task resume]; } } - (void)stopLoading { [self.task cancel]; [self setTask:nil]; [self setData:nil]; [self setResponse:nil]; } #pragma mark - NSURLSession delegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler { if (response !=nil) { NSMutableURLRequest *redirectableRequest = [newRequest mutableCopy]; [[self class] removePropertyForKey:kOurRecursiveRequestFlagProperty inRequest:redirectableRequest]; NSString *cachePath = [self cachePathForRequest:[self request]]; WebCachedData *cache = [[WebCachedData alloc] init]; [cache setResponse:response]; [cache setData:[self data]]; [cache setDate:[NSDate date]]; [cache setRedirectRequest:redirectableRequest]; [NSKeyedArchiver archiveRootObject:cache toFile:cachePath]; [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response]; [self.task cancel]; [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]]; completionHandler(redirectableRequest); } else { completionHandler(newRequest); } } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void(^)(NSURLSessionResponseDisposition))completionHandler { [self setResponse:response]; [self setData:nil]; [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; completionHandler(NSURLSessionResponseAllow); } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [[self client] URLProtocol:self didLoadData:data]; [self appendData:data]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)dataTask didCompleteWithError:(NSError *)error { if (error ) { [self.client URLProtocol:self didFailWithError:error]; } else { NSString *cachePath = [self cachePathForRequest:[self request]]; WebCachedData *cache = [[WebCachedData alloc] init]; [cache setResponse:[self response]]; [cache setData:[self data]]; [cache setDate:[NSDate date]]; [NSKeyedArchiver archiveRootObject:cache toFile:cachePath]; [[self client] URLProtocolDidFinishLoading:self]; } } #pragma mark - private APIs + (NSArray *)ignoreURLs { NSArray *iURLs; @synchronized(CacheURLProtocolIgnoreURLsMonitor) { iURLs = CacheURLProtocolIgnoreURLs; } return iURLs; } + (void)setIgnoreURLs:(NSArray *)iURLs { @synchronized(CacheURLProtocolIgnoreURLsMonitor) { CacheURLProtocolIgnoreURLs = iURLs; } } + (BOOL)canInitWithURLRequest:(NSURLRequest*)request { // 过滤掉不需要走URLProtocol NSArray *ignores = [self ignoreURLs]; for (NSString *url in ignores) { if ([[request.URL absoluteString] hasPrefix:url]) { return NO; } } // 如果是startLoading里发起的request忽略掉,避免死循环 BOOL recurisve = [self propertyForKey:kOurRecursiveRequestFlagProperty inRequest:request] == nil; // 没有标识位kOurRecursiveRequestFlagProperty的并且是以http开的scheme都走代理; if (recurisve && [[request.URL scheme] hasPrefix:@"http"]) { return YES; } return NO; } - (void)configProtocolParam { NSURLSessionConfiguration *config = [[NSURLSessionConfiguration defaultSessionConfiguration] copy]; [config setProtocolClasses:@[ [self class] ]]; [self setConfiguration:config]; NSOperationQueue *q = [[NSOperationQueue alloc] init]; [q setMaxConcurrentOperationCount:1]; [q setName:kSessionQueueName]; [self setSessionQueue:q]; NSURLSession *s = [NSURLSession sessionWithConfiguration:_configuration delegate:self delegateQueue:_sessionQueue]; s.sessionDescription =kSessionDescription; [self setSession:s]; } - (NSString*)md5Encode:(NSString*)srcString { const char *cStr = [srcString UTF8String]; unsigned char result[16]; CC_MD5( cStr, (unsigned int)strlen(cStr), result); NSString *formatString =@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"; return [NSString stringWithFormat:formatString, result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]]; } - (NSString *)cachePathForRequest:(NSURLRequest *)aRequest { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES)lastObject]; NSString *fileName = [self md5Encode:[[aRequest URL]absoluteString]]; return [cachesPath stringByAppendingPathComponent:fileName]; } - (void)appendData:(NSData *)newData { if ([self data] == nil) { self.data = [[NSMutableData alloc] initWithCapacity:0]; } if (newData) { [[self data] appendData:newData]; } } - (BOOL)expireCacheData:(NSDate *)date { if (!date) { return YES; } NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:date]; BOOL bRet = timeInterval <kCacheExpireTime; if (!bRet) { // 过期删除缓存 NSString *filename = [self cachePathForRequest:[self request]]; NSFileManager *defaultManager = [NSFileManager defaultManager]; if ([defaultManager isDeletableFileAtPath:filename]) { [defaultManager removeItemAtPath:filename error:nil]; } } return !bRet; } @end