利用NSURLProtocol实现webView缓存

注册自定义protocol handler MyURLProtocol
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[MyURLProtocol class]];
    return YES;
}

几个重要的NSURLProtocol方法
+ ( BOOL ) canInitWithRequest : ( NSURLRequest * ) request;
当URL loading System接收到一个请求时,系统会去寻找一个注册过的protocol handler来处理这个请求。每一个protocol handler通过该方法告诉系统是否可以处理该请求。
return YES ,系统将会依赖该protocol handler 来处理这个请求。
如果所有的自定义protocol handler都返回NO, 系统会用自己的默认行为来处理这个请求。

如果你自定义了一个新的protocol foo:// 你可以在这里判断是否这个URL scheme 是foo。如果是,用自定义protocol handler 返回YES,来处理。

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
NSURLProtocol的抽象方法,子类必须进行实现。返回规范化后的request,这个方法给了我们重新处理request的机会,比如如果我们需要为request加一个header那么在这里会是一个不错的时机。

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;
在这里可以指定两个不同的请求,视为同一个请求。它们会使用相同的缓存数据

- (void)startLoading和 -(void)stopLoading
开始请求数据和结束请求数据

几个NSURLConnection的代理方法

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {    [self.client URLProtocol:self didLoadData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection {    [self.client URLProtocolDidFinishLoading:self]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {    [self.client URLProtocol:self didFailWithError:error]; }
在这些代理方法中我们拿到请求的进展过程,并通过urlprotocol handler 的client 将进展告诉系统,从而对系统进行代理。

运行程序,发生了什么? 死循环!
解决办法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    static NSUInteger requestCount = 0;
    NSLog(@"Request #%u: URL = %@", requestCount++, request);
 
    if ([NSURLProtocol propertyForKey:@"MyURLProtocolHandledKey" inRequest:request]) {
        return NO;
    }
 
    return YES;
}
 
- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest];
 
    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}

本地缓存网络请求
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
 
    self.response = response;
    self.mutableData = [[NSMutableData alloc] init];
}
 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    [self.mutableData appendData:data];
}
 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
    [self saveCachedResponse];
}




- (void)saveCachedResponse {
    NSLog(@"saving cached response");
 
    // 1.
    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = delegate.managedObjectContext;
 
    // 2.
    CachedURLResponse *cachedResponse = [NSEntityDescription insertNewObjectForEntityForName:@"CachedURLResponse"
                                                                      inManagedObjectContext:context];
    cachedResponse.data = self.mutableData;
    cachedResponse.url = self.request.URL.absoluteString;
    cachedResponse.timestamp = [NSDate date];
    cachedResponse.mimeType = self.response.MIMEType;
    cachedResponse.encoding = self.response.textEncodingName;
 
    // 3.
    NSError *error;
    BOOL const success = [context save:&error];
    if (!success) {
        NSLog(@"Could not cache the response.");
    }
}

- (CachedURLResponse *)cachedResponseForCurrentRequest {
    // 1.
    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = delegate.managedObjectContext;
 
    // 2.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"CachedURLResponse"
                                              inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
 
    // 3.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"url == %@", self.request.URL.absoluteString];
    [fetchRequest setPredicate:predicate];
 
    // 4.
    NSError *error;
    NSArray *result = [context executeFetchRequest:fetchRequest error:&error];
 
    // 5.
    if (result && result.count > 0) {
        return result[0];
    }
 
    return nil;
}


- (void)startLoading {
    // 1.
    CachedURLResponse *cachedResponse = [self cachedResponseForCurrentRequest];
    if (cachedResponse) {
        NSLog(@"serving response from cache");
 
        // 2.
        NSData *data = cachedResponse.data;
        NSString *mimeType = cachedResponse.mimeType;
        NSString *encoding = cachedResponse.encoding;
 
        // 3.
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL
                                                            MIMEType:mimeType
                                               expectedContentLength:data.length
                                                    textEncodingName:encoding];
 
        // 4.
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocolDidFinishLoading:self];
    } else {
        // 5.
        NSLog(@"serving response from NSURLConnection");
 
        NSMutableURLRequest *newRequest = [self.request mutableCopy];
        [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest];
 
        self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值