iOS中,關(guān)于UIWebView網(wǎng)頁(yè)數(shù)據(jù)本地緩存原理和實(shí)際使用。

? ? ?最近作者做的項(xiàng)目中需要用到UIWebView的離線緩存功能,本來(lái)滿(mǎn)心歡喜的想著在UIWebView的代理方法中看看有沒(méi)有什么代理方法可以直接做到緩存的功能,結(jié)果還是太天真了,后來(lái)網(wǎng)上搜索了一下(主要參考了在code4app上面rusking作業(yè)對(duì)UIWebView離線瀏覽的代碼實(shí)現(xiàn)(地址https://github.com/lzhlewis2015/UIWebViewLocalCache),研究的過(guò)程中也花了不少時(shí)間,所以想在這里把我的心得分享一下),發(fā)現(xiàn)可以使用NSURLCache這個(gè)類(lèi)實(shí)現(xiàn)。原理就是大多數(shù)的網(wǎng)絡(luò)請(qǐng)求都會(huì)先調(diào)用這個(gè)類(lèi)中的- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request 這個(gè)方法,那我們只要重寫(xiě)這個(gè)類(lèi),就能達(dá)到本地緩存的目的了。

下面是大致的邏輯

1 判斷請(qǐng)求中的request 是不是使用get方法,據(jù)資料顯示一些本地請(qǐng)求的協(xié)議也會(huì)進(jìn)到這個(gè)方法里面來(lái),所以在第一部,要把不相關(guān)的請(qǐng)求排除掉。

2 判斷緩存文件夾里面是否存在該文件,如果存在,繼續(xù)判斷文件是否過(guò)期,如果過(guò)期,則刪除。如果文件沒(méi)有過(guò)期,則提取文件,然后組成NSCacheURLResponse返回到方法當(dāng)中。

3在有網(wǎng)絡(luò)的情況下,如果文件夾中不存在該文件,則利用NSConnection這個(gè)類(lèi)發(fā)網(wǎng)絡(luò)請(qǐng)求,再把返回的data和response 數(shù)據(jù)本地化存儲(chǔ)起來(lái),然后組成NSCacheURLResponse返回到方法當(dāng)中。

4其中BaseTools和其他沒(méi)有在本.m文件中定義的類(lèi)為常用的工具類(lèi),這里不一一展開(kāi)了。


大致邏輯就這么多,話(huà)不多說(shuō),直接看代碼實(shí)現(xiàn)(關(guān)鍵代碼有注釋?zhuān)?/p>

#import "CustomURLCache.h"

#import "NSObject+Network.h"

#import "BaseTools.h"

@interface CustomURLCache(private)

- (NSString *)cacheFolder;

- (NSString *)cacheFilePath:(NSString *)file;

- (NSString *)cacheRequestFileName:(NSString *)requestUrl;

- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl;

- (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request;

- (void)deleteCacheFolder;

@end

@implementation CustomURLCache

- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime {

if (self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) {

//cacheTime 為你所希望本地緩存的時(shí)間(以秒計(jì)算,如果設(shè)為60,則60秒之后本地緩存文件過(guò)期)

self.cacheTime = cacheTime;

if (path)

self.diskPath = path;

else ? ?

self.diskPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

self.responseDictionary = [NSMutableDictionary dictionaryWithCapacity:0];

}

return self;

}//


- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {

// 這里判斷如果請(qǐng)求方法不為GET的話(huà) 直接返回父類(lèi)方法,系統(tǒng)本來(lái)怎么干的就讓它怎么干

if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {

return [super cachedResponseForRequest:request];

}

// 核心方法

return [self dataFromRequest:request];

}//


- (void)removeAllCachedResponses {

[super removeAllCachedResponses];

[self deleteCacheFolder];

}//

- (void)removeCachedResponseForRequest:(NSURLRequest *)request {

[super removeCachedResponseForRequest:request];

NSString *url = request.URL.absoluteString;

NSString *fileName = [self cacheRequestFileName:url];

NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url];

NSString *filePath = [self cacheFilePath:fileName];

NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName];

NSFileManager *fileManager = [NSFileManager defaultManager];


[fileManager removeItemAtPath:filePath error:nil];

[fileManager removeItemAtPath:otherInfoPath error:nil];

}//

#pragma mark - custom url cache

- (NSString *)cacheFolder {

return @"URLCACHE";

}//

- (void)deleteCacheFolder {

NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]];

NSFileManager *fileManager = [NSFileManager defaultManager];

[fileManager removeItemAtPath:path error:nil];

}//

- (NSString *)cacheFilePath:(NSString *)file {

NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]];

NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isDir;

if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir) {

} else {

[fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];

}

return [NSString stringWithFormat:@"%@/%@", path, file];

}//

- (NSString *)cacheRequestFileName:(NSString *)requestUrl {

//對(duì)傳進(jìn)來(lái)的url進(jìn)行md5 加密 ,加密后變成32位字符串,作為文件名保存

return [BaseTools md5Hash:requestUrl];

}//

- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl {

//同上

return [BaseTools md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]];

}//

- (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request {

//此為GET的情況

// 這方法會(huì)返回多次 每一次鏈接相同的url(有網(wǎng)絡(luò)的情況下,部分網(wǎng)頁(yè)如:(百度),它這個(gè)url里面可能內(nèi)嵌了很多其他的url,那其他的url可能每次都不一樣,所以返回的request.url.absluteString 都不一樣,這樣導(dǎo)致每次系統(tǒng)會(huì)根據(jù)absoluteStr 來(lái)創(chuàng)建文件,則會(huì)越來(lái)越多;但針對(duì)作業(yè)的App里面涉及到的網(wǎng)頁(yè)鏈接不會(huì)這樣。

NSString *url = request.URL.absoluteString;

//md5 加密

NSString *fileName = [self cacheRequestFileName:url];

NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url];

//filePath 用于保存網(wǎng)頁(yè)數(shù)據(jù)

NSString *filePath = [self cacheFilePath:fileName];

//otherInfoPath ?用于保存該url 對(duì)應(yīng)的一些配置屬性,如創(chuàng)建時(shí)間,MIMEType等。。

NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName];

NSDate *date = [NSDate date];

NSFileManager *fileManager = [NSFileManager defaultManager];

if ([fileManager fileExistsAtPath:filePath]) {

// expire 為過(guò)期的

BOOL expire = false;

NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:[otherInfoPath stringByAppendingString:@".plist"]];

//cacheTime ?為磁盤(pán)緩存文件在硬盤(pán)中保存的時(shí)間

//cacheTime 為0 時(shí)則永遠(yuǎn)不會(huì)過(guò)期

if (self.cacheTime > 0) {

NSInteger createTime = [[otherInfo objectForKey:@"time"] intValue];

if (createTime + self.cacheTime < [date timeIntervalSince1970]) {

expire = true;

}

}

if (expire == false ) {

NSLog(@"data from cache ...");

//發(fā)現(xiàn)緩存文件夾里面有緩存在硬盤(pán)的文件

NSData *data = [NSData dataWithContentsOfFile:filePath];

NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL

MIMEType:[otherInfo objectForKey:@"MIMEType"]

expectedContentLength:data.length

textEncodingName:[otherInfo objectForKey:@"textEncodingName"]];

NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data] ;

return cachedResponse;

} else {

NSLog(@"cache expire ... ");

//過(guò)期了要?jiǎng)h除

[fileManager removeItemAtPath:filePath error:nil];

[fileManager removeItemAtPath:[NSString stringWithFormat:@"%@",[otherInfoPath stringByAppendingString:@".plist"]] error:nil];


}

}

if (![self isReachability]) {

return nil;

}

// 有網(wǎng)絡(luò)的狀態(tài)下進(jìn)行內(nèi)容的緩存

__block NSCachedURLResponse *cachedResponse = nil;

//sendAsynchronousRequest請(qǐng)求也要經(jīng)過(guò)NSURLCache

//如果沒(méi)有response 和data 的話(huà), 那字典對(duì)應(yīng)的value 為true,方法直接返回nil(此鏈接不能使用緩存);

id boolExsit = [self.responseDictionary objectForKey:url];

if (boolExsit == nil) {

[self.responseDictionary setValue:[NSNumber numberWithBool:TRUE] forKey:url];

[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data,NSError *error)

{

// 如果有data 和response 返回的話(huà)

if (response && data) {

//因?yàn)閏achesResponse 這個(gè)方法會(huì)被調(diào)用多次,所有dataFromrequest也會(huì)被調(diào)用多次,那如果服務(wù)器返回有response 和data的話(huà),就把responDicionary 這個(gè)字典清空,并把對(duì)應(yīng)的data寫(xiě)入,并把對(duì)應(yīng)的data和response 構(gòu)建成Cacheresponse 返回

[self.responseDictionary removeObjectForKey:url];

if (error) {

NSLog(@"error : %@", error);

NSLog(@"not cached: %@", request.URL.absoluteString);

cachedResponse = nil;

}

NSLog(@"---");

//save to cache

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f", [date timeIntervalSince1970]], @"time",

response.MIMEType, @"MIMEType",

response.textEncodingName, @"textEncodingName", nil];

BOOL dictSuccess = [dict writeToFile:[otherInfoPath stringByAppendingString:@".plist"] atomically:YES];

BOOL dataSuccess = [data writeToFile:filePath atomically:YES];

if (!dictSuccess) {

NSLog(@"字典失敗");

}

if (!dataSuccess) {

NSLog(@"data 失敗");

}

cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data] ;

}

}];

return cachedResponse;

}

return nil;

} //

@end



鑒于挺多讀者可能看不到demo的下載地址,這里再列一下

https://github.com/lzhlewis2015/UIWebViewLocalCache

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容