NSURLProtocol是iOS中 URL Loading System 的一部分。若是開發者自定義的一個NSURLProtocol而且註冊到app中,那麼在這個自定義的NSURLProtocol中咱們能夠攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網絡請求,而後作到自定義的response返回。很是強大。web
推薦一個iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入面試
好比我這邊自定義的NSURLProtocol叫作YXNSURLProtocol。編程
@interface YXNSURLProtocol : NSURLProtocol
@end複製代碼
在系統加載的時候,把自定義的YXNSURLProtocol註冊到URL加載系統中,這樣 全部的URL請求都有機會進入咱們自定義的YXNSURLProtocol進行攔截處理。緩存
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[NSURLProtocol registerClass:[YXNSURLProtocol class]];
}複製代碼
加載完成後,當產生URL請求的同時,會依次進入NSURLProtocol的如下相關方法進行處理,下面咱們依次來說一下每個方法的做用。bash
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
BOOL intercept = YES;
NSLog(@"YXNSURLProtocol==%@",request.URL.absoluteString);
if (intercept) {
}
return intercept;
}複製代碼
若是返回YES則進入該自定義加載器進行處理,若是返回NO則不進入該自定義選擇器,使用系統默認行爲進行處理。 若是這一步驟返回YES。則會進入2.3的方法中。網絡
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}複製代碼
在這個方法中,咱們能夠從新設置或者修改request的信息。好比請求重定向或者添加頭部信息等等。若是沒有特殊需求,直接返回request就能夠了。可是由於這個方法在會在一次請求中被調用屢次(暫時我也不知道什麼緣由爲何須要回調多洗),因此request重定向和添加頭部信息也能夠在開始加載中startLoading方法中從新設置。app
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{
return [super requestIsCacheEquivalent:a toRequest:b];
}複製代碼
這個方法基本不經常使用。函數
2.2.四、被攔截的請求開始執行的地方學習
這個函數使咱們重點使用的函數。測試
- (void)stopLoading{
}複製代碼
上面的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複製代碼
這個Demo實現的功能是在UIWebView中全部跳轉到sina首頁的請求,都重定位到sohu首頁。
_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];複製代碼
@interface YXNSURLProtocolTwo : NSURLProtocol
@end複製代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[NSURLProtocol registerClass:[YXNSURLProtocolTwo class]];
return YES;
}複製代碼
+ (BOOL)canInitWithRequest:(NSURLRequest *)request{
NSLog(@"canInitWithRequest url-->%@",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;
}複製代碼
- (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。效果都是同樣的。
- (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加載新浪首頁,卻跳轉到了搜狐首頁。
經過自定義的NSURLProtocol,咱們拿到用戶請求的request以後,咱們能夠作不少事情。好比: 一、自定義請求和響應 二、網絡的緩存處理(H5離線包 和 網絡圖片緩存) 三、重定向網絡請求 四、爲測試提供數據Mocking功能,在沒有網絡的狀況下使用本地數據返回。 五、過濾掉一些非法請求 六、快速進行測試環境的切換 七、攔截圖片加載請求,轉爲從本地文件加載 八、能夠攔截UIWebView,基於系統的NSURLConnection或者NSURLSession進行封裝的網絡請求。目前WKWebView沒法被NSURLProtocol攔截。 九、當有多個自定義NSURLProtocol註冊到系統中的話,會按照他們註冊的反向順序依次調用URL加載流程。當其中有一個NSURLProtocol攔截到請求的話,後續的NSURLProtocol就沒法攔截到該請求。
推薦一個iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入
若是以爲對你還有些用,就關注小編+喜歡這一篇文章。你的支持是我繼續的動力。
下篇文章預告:iOS 面向切面編程的實現與實戰案例
文章來源於網絡,若有侵權,請聯繫小編刪除。