码迷,mamicode.com
首页 > Web开发 > 详细

NSURLProtocol

时间:2015-09-23 17:25:53      阅读:276      评论:0      收藏:0      [点我收藏+]

标签:

启动时注册 NSURLProtocol 类的实现类 MyURLProtocol

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    
    [NSURLProtocol registerClass:MyURLProtocol.class];
    
    return YES;
}


UI 界面中,从一个文本框输入url 发送请求:

#pragma mark - IBAction

- (IBAction)buttonGoClicked:(id)sender {
    
    if ([self.textField isFirstResponder]) {
        [self.textField resignFirstResponder];
    }
    
    [self sendRequest];
}

#pragma mark - UITextFieldDelegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    
    [self sendRequest];
    
    return YES;
}

#pragma mark - Private

- (void) sendRequest {
    
    NSString *text = self.textField.text;
    if (![text isEqualToString:@""]) {
        
        NSURL *url = [NSURL URLWithString:text];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [self.webView loadRequest:request];
        
    }
    
}


之后就是 MyURLPotocol 的部分了:

MyURLProtocolHandledKey是一个常亮字符串,用来标识一个request是否应该被拦截.


首先是canInitWithRequest:方法:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    if ([NSURLProtocol propertyForKey:MyURLProtocolHandledKey inRequest:request]) {
        NSLog(@"NO");
        return NO;
    }
    NSLog(@"YES");
    return YES;
}

如果canInitWithRequest:返回NO, 就表示 当前注册的MyURLPotocol 不能处理这个请求, MyURLPotocol 就会被绕过去,就像不存在一样,原来的request 按照它本来的逻辑去加载;

如果canInitWithRequest: 返回YES,就表示 当前注册的MyURLPotocol 可以处理这个请求,接下来会调用 MyURLPotocol 的 canonicalRequestForRequest:方法,startLoading 方法等。


接下来是关键的startLoading 方法:

startLoading 里面是我们拦截下请求后自己的处理逻辑, 

1)首先检测是否有符合当前request的缓存对象(使用CoreData保存),如果有的话就把缓存对象作为请求的返回,并显示调用client ( NSURLProtocolClient类型,和 NSURLConnectionDelegate非常相似)唤起 URL Loading System 的回调。注意缓存规则使用了 不允许缓存 , 因为我们使用已经CoreData进行缓存了。

2)如果没有缓存对象, 则复制当前request ,并设置 MyURLProtocolHandledKey 标志,告诉 MyURLPotocol 不拦截这个请求( 因为所有的url 请求都会通过 NSURLProtocol的 canInitWithRequest:方法的检测),最后通过正常的NSURLConnection 或 NSURLSession 发送请求。

NSURLConnection 需要一个  NSURLConnectionDelegate 来接收 事件的回调(响应、数据、成功、失败等事件),这里MyURLPotocol 实现了 NSURLConnectionDelegate,所以注册为 self。

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


NSURLConnectionDelegate 方法的实现:

在这些方法中,让然要通过client对象通知 URL Loading System

在connectiongDidFinishLoading:方法里,使用CoreData 保存请求返回的数据。

#pragma mark - NSURLConnectionDelegate
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSLog(@"=> connection: didReceiveResponse: ");
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    self.response = response;
    self.mutableData = [[NSMutableData alloc] init];
}

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    NSLog(@"=> == =====connection: didReceiveData:  =======");
    [self.client URLProtocol:self didLoadData:data];
    [self.mutableData appendData:data];
}

- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
    [self saveCachedResponse];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
}


技术分享      


请求的过程如图所示, 虚线表示 请求被拦截后发现找不到本地缓存,需要通过 URLConnection 发送网络请求的情况:技术分享

NSURLProtocol

标签:

原文地址:http://my.oschina.net/u/255456/blog/510109

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!