iOS H5容器的一些探究(二):iOS下的黑魔法NSURLProtocol

1、前言

NSURLProtocol是iOS中 URL Loading System 的一部分。若是開發者自定義的一個NSURLProtocol而且註冊到app中,那麼在這個自定義的NSURLProtocol中咱們能夠攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網絡請求,而後作到自定義的response返回。很是強大。web

推薦一個iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入面試

2、NSURLProtocol的使用流程

2.一、在AppDelegate中註冊自定義的NSURLProtocol。

好比我這邊自定義的NSURLProtocol叫作YXNSURLProtocol。編程

@interface YXNSURLProtocol : NSURLProtocol
@end複製代碼

在系統加載的時候,把自定義的YXNSURLProtocol註冊到URL加載系統中,這樣 全部的URL請求都有機會進入咱們自定義的YXNSURLProtocol進行攔截處理。緩存

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[YXNSURLProtocol class]];
}複製代碼

加載完成後,當產生URL請求的同時,會依次進入NSURLProtocol的如下相關方法進行處理,下面咱們依次來說一下每個方法的做用。bash

2.二、NSURLProtocol中的幾個方法

2.2.一、是否進入自定義的NSURLProtocol加載器

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
    BOOL intercept = YES;
    NSLog(@"YXNSURLProtocol==%@",request.URL.absoluteString);
    if (intercept) {

    }
    return intercept;
}複製代碼

若是返回YES則進入該自定義加載器進行處理,若是返回NO則不進入該自定義選擇器,使用系統默認行爲進行處理。 若是這一步驟返回YES。則會進入2.3的方法中。網絡

2.2.二、從新設置NSURLRequest的信息

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}複製代碼

在這個方法中,咱們能夠從新設置或者修改request的信息。好比請求重定向或者添加頭部信息等等。若是沒有特殊需求,直接返回request就能夠了。可是由於這個方法在會在一次請求中被調用屢次(暫時我也不知道什麼緣由爲何須要回調多洗),因此request重定向和添加頭部信息也能夠在開始加載中startLoading方法中從新設置。app

2.2.三、這個方法主要是用來判斷兩個request是否相同,若是相同的話可使用緩存數據,一般調用父類的實現便可

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{
    return [super requestIsCacheEquivalent:a toRequest:b];
}複製代碼

這個方法基本不經常使用。函數

2.2.四、被攔截的請求開始執行的地方學習

  • (void)startLoading{ }

這個函數使咱們重點使用的函數。測試

2.2.五、結束加載URL請求

- (void)stopLoading{
}複製代碼

2.三、NSURLProtocolClient中的幾個方法

上面的NSURLProtocol定義了一系列加載的流程。而在每個流程中,咱們做爲使用者該如何使用URL加載系統,則是NSURLProtocolClient中幾個方法該作的事情。

@protocol NSURLProtocolClient <NSObject>

//請求重定向
- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;

// 響應緩存是否合法
- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;

//剛接收到Response信息
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

//數據加載成功
- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

//數據完成加載
- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

//數據加載失敗
- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;

//爲指定的請求啓動驗證
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

//爲指定的請求取消驗證
- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

@end複製代碼

3、實現一個地址重定向的Demo

這個Demo實現的功能是在UIWebView中全部跳轉到sina首頁的請求,都重定位到sohu首頁。

3.一、第一步,新建一個UIWebView,加載sina首頁

_webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    _webView.delegate = self;
    [self.view addSubview:_webView];
    NSURL *url = [[NSURL alloc] initWithString:@"https://sina.cn"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [_webView loadRequest:request];複製代碼

3.二、自定義一個NSURLProtocol

@interface YXNSURLProtocolTwo : NSURLProtocol

@end複製代碼

3.三、在AppDelegate中,進行註冊

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[YXNSURLProtocolTwo class]];
    return YES;
}複製代碼

3.四、在canInitWithRequest方法中攔截https://sina.cn/

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
    NSLog(@"canInitWithRequest url--&gt;%@",request.URL.absoluteString);

    //看看是否已經處理過了,防止無限循環
    if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
        return NO;
    }

    NSString *urlString = request.URL.absoluteString;
    if([urlString isEqualToString:@"https://sina.cn/"]){
        return YES;
    }

    return NO;
}複製代碼

3.五、在startLoading中進行方法重定向

- (void)startLoading{

    NSMutableURLRequest * request = [self.request mutableCopy];

    // 標記當前傳入的Request已經被攔截處理過,
    //防止在最開始又繼續攔截處理
    [NSURLProtocol setProperty:@(YES) forKey:URLProtocolHandledKey inRequest:request];

    self.connection = [NSURLConnection connectionWithRequest:[self changeSinaToSohu:request] delegate:self];
}

//把所用url中包括sina的url重定向到sohu
- (NSMutableURLRequest *)changeSinaToSohu:(NSMutableURLRequest *)request{
    NSString *urlString = request.URL.absoluteString;
    if ([urlString isEqualToString:@"https://sina.cn/"]) {
        urlString = @"http://m.sohu.com/";
        request.URL = [NSURL URLWithString:urlString];
    }
    return request;
}複製代碼

你也能夠選擇在+ (NSURLRequest

)canonicalRequestForRequest:(NSURLRequest
)request替換request。效果都是同樣的。

3.六、由於新建了一個NSURLConnection *connection,因此要實現他的代理方法,以下

- (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];
}複製代碼

經過以上幾步,咱們就能夠實現最簡單的url重定向,WebView加載新浪首頁,卻跳轉到了搜狐首頁。

4、小結

經過自定義的NSURLProtocol,咱們拿到用戶請求的request以後,咱們能夠作不少事情。好比: 一、自定義請求和響應 二、網絡的緩存處理(H5離線包 和 網絡圖片緩存) 三、重定向網絡請求 四、爲測試提供數據Mocking功能,在沒有網絡的狀況下使用本地數據返回。 五、過濾掉一些非法請求 六、快速進行測試環境的切換 七、攔截圖片加載請求,轉爲從本地文件加載 八、能夠攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網絡請求。目前WKWebView沒法被NSURLProtocol攔截。 九、當有多個自定義NSURLProtocol註冊到系統中的話,會按照他們註冊的反向順序依次調用URL加載流程。當其中有一個NSURLProtocol攔截到請求的話,後續的NSURLProtocol就沒法攔截到該請求。

5、聯繫方式

推薦一個iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入

若是以爲對你還有些用,就關注小編+喜歡這一篇文章。你的支持是我繼續的動力。

下篇文章預告:iOS 面向切面編程的實現與實戰案例

文章來源於網絡,若有侵權,請聯繫小編刪除。

相關文章
相關標籤/搜索