iOS进阶1--UIWebView缓存

大家有没有遇到过项目有要求对UIWebView的请求数据进行缓存,在有缓存的情况下,从本地加载数据,如果没有缓存,或者缓存已经过期的情况下,则从服务端进行加载数据,并对其返还结果进行缓存操作,如果遇到这种需求,大家会有怎样的解决方案呢?以下是参考http://www.jianshu.com/p/7f3be7c30c77的解决方案。

第一种方案:
原理就是大多数的网络请求都会先调用这个类中的- (NSCachedURLResponse )cachedResponseForRequest:(NSURLRequest )request 这个方法,那我们只要重写这个类,就能达到本地缓存的目的了。
下面是大致的逻辑
1.判断请求中的request 是不是使用get方法,据资料显示一些本地请求的协议也会进到这个方法里面来,所以在第一部,要把不相关的请求排除掉。
2.判断缓存文件夹里面是否存在该文件,如果存在,继续判断文件是否过期,如果过期,则删除。如果文件没有过期,则提取文件,然后组成NSCacheURLResponse返回到方法当中。
3.在有网络的情况下,如果文件夹中不存在该文件,则利用NSConnection这个类发网络请求,再把返回的data和response 数据本地化存储起来,然后组成NSCacheURLResponse返回到方法当中。
具体代码如下:
1.定义一个CustomURLCache 继承于NSURLCache

@interface CustomURLCache : NSURLCache

@property(nonatomic, assign) NSInteger cacheTime;
@property(nonatomic, retain) NSString *diskPath;
@property(nonatomic, retain) NSMutableDictionary *responseDictionary;

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

@end

.m文件如下:

#import "CustomURLCache.h"
#import "Reachability.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

@synthesize cacheTime = _cacheTime;
@synthesize diskPath = _diskPath;
@synthesize responseDictionary = _responseDictionary;

/**初始化方法
 @param memoryCapacity 缓存内存大小,默认4M,4* 1024 * 1024
 @param diskCapacity 硬盘大小,默认20M,20 * 1024 * 1024
 @param path 缓存路径,默认(NSHomeDirectory)/Library/Caches/(current application name, [[NSProcessInfo processInfo] processName])
 @param cacheTime 缓存有效时间
 @return 自定义NSURLCache对象
 */

- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime {
    if (self = [self initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) {
        self.cacheTime = cacheTime;
        if (path)
            self.diskPath = path;
        else
            self.diskPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

        self.responseDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
    }
    return self;
}

- (void)dealloc {
    [_diskPath release];
    [_responseDictionary release];
    [super dealloc];
}

//所有网络请求都会优先调用该方法,所有我们只要在这里重载这个方法,对请求进行处理即可
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    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 {
    return [Util md5Hash:requestUrl];
}

- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl {
    return [Util md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]];
}

/*关键方法,如果没有缓存,则返回nil,有则返回一个NSCachedURLResponse对象*/
- (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)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];//其它文件路径
    NSDate *date = [NSDate date];//缓存操作时间

    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:filePath]) {
        //缓存文件夹已经存在
        BOOL expire = false;
        NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:otherInfoPath];

        if (self.cacheTime > 0) {
        //cacheTime == 0,则表示永久有效
            NSInteger createTime = [[otherInfo objectForKey:@"time"] intValue];
            if (createTime + self.cacheTime < [date timeIntervalSince1970]) {
                expire = true;
            }
        }

        if (expire == false) {
            NSLog(@"data from cache ...");
            //从缓存读取数据,初始化一个NSCachedURLResponse对象
            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] autorelease];
            [response release];
            return cachedResponse;
        } else {
            NSLog(@"cache expire ... ");
            //缓存过期,移除已经缓存的数据
            [fileManager removeItemAtPath:filePath error:nil];
            [fileManager removeItemAtPath:otherInfoPath error:nil];
        }
    }
    if (![Reachability networkAvailable]) {
        return nil;
    }

    //sendSynchronousRequest请求也要经过NSURLCache,从新进行数据缓存
    __block NSCachedURLResponse * cachedResponse = nil;
    id boolExsite = [self.responseDictionary objectForKey:url];
    if (boolExsite == nil) {
        [self.responseDictionary setValue:[NSNumber numberWithBool:TRUE] forKey:url];

       // NSURLResponse *response = nil;
//        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

        [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
            if (response && data) {
                [self.responseDictionary removeObjectForKey:url];
            }
            if (connectionError) {
                NSLog(@"error : %@", connectionError);
                NSLog(@"not cached: %@", request.URL.absoluteString);
                cachedResponse = nil;
            }

            NSLog(@"get request ... ");

            //save to cache
            NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f", [date timeIntervalSince1970]], @"time",
                                  response.MIMEType, @"MIMEType",
                                  response.textEncodingName, @"textEncodingName", nil];
            [dict writeToFile:otherInfoPath atomically:YES];
            [data writeToFile:filePath atomically:YES];

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





            }];



                return cachedResponse;
    }
    return nil;
}


@end

具体使用如下:

//关键:类初始化时进行,改变默认的NSURLCache
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
                                                                     diskCapacity:200 * 1024 * 1024
                                                                         diskPath:nil
                                                                        cacheTime:0];
         //setSharedURLCache该方法可以使缓存类变为我们自定义的缓存方案                                              
        [CustomURLCache setSharedURLCache:urlCache];
        [urlCache release];
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.frame];
    webView.delegate = self;
    self.webView = webView;
    [webView release];
    [self.view addSubview:_webView];

    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]]];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.

    CustomURLCache *urlCache = (CustomURLCache *)[NSURLCache sharedURLCache];
    [urlCache removeAllCachedResponses];
}

- (void)dealloc {
    [_webView release];
    [super dealloc];
}

#pragma mark - webview

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [MBProgressHUD hideHUDForView:self.view animated:YES];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    [MBProgressHUD hideHUDForView:self.view animated:YES];
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.mode = MBProgressHUDModeIndeterminate;
    hud.labelText = @"Loading...";
}

注意: 这方案会返回多次 每一次链接相同的url(有网络的情况下,部分网页如:(百度),它这个url里面可能内嵌了很多其他的url,那其他的url可能每次都不一样,所以返回的request.url.absluteString 都不一样,这样导致每次系统会根据absoluteStr 来创建文件,则会越来越多;这种情况下是不适用的。

源码地址:https://github.com/lzhlewis2015/UIWebViewLocalCache

第二种方案:

+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;
//通过上面方法进行设置NSURLRequestCachePolicy

- (void)loadWebView
{
   //可以每次请求的时候先去缓存里面读取,如果缓存里面没有的话再去网上加载

    request = [NSURLRequest requestWithURL:urlcachePolicy:NSURLRequestReturnCacheDataElseLad timeoutInterval:10.];

    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
    if (cachedResponse) {
     //cache已经存在 直接用web加载本地的数据
       NSLog(@"cache存在 直接加载本地的数据");
      [_WKWebView loadHTMLString:[[NSString alloc]initWithData:cachedResponse.data encoding:NSUTF8StringEncoding] baseURL:_request.URL];}
}else {
    //cache还不存在
    NSLog(@"cache不存在 需要从网络加载");

    NSURLSessionDownloadTask *task = [[NSURLSession sharedSession]downloadTaskWithRequest:_request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    NSData *data = [NSData dataWithContentsOfURL:location];
    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:_request];
    if ((!cachedResponse) && response && data) {
     NSCachedURLResponse *cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
    [[NSURLCache sharedURLCache]storeCachedResponse:cachedURLResponse forRequest:_request];}
 }];
   [task resume];

   [_WKWebView loadRequest:request];}
}

//这个清除缓存也很简单 
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值