什么是 NSURLProtocol
NSURLProtocol作為URL Loading System中的一個獨立部分存在物邑,能夠攔截所有的URL Loading System發(fā)出的網(wǎng)絡(luò)請求猴凹,攔截之后便可根據(jù)需要做各種自定義處理扔亥,是iOS網(wǎng)絡(luò)層實現(xiàn)AOP(面向切面編程)的終極利器
NSURLProtocol是URL Loading System的重要組成部分帆竹。
首先雖然名叫NSURLProtocol杭煎,但它卻不是協(xié)議哼拔。它是一個抽象類概行。我們要使用它的時候需要創(chuàng)建它的一個子類
NSURLProtocol在iOS系統(tǒng)中大概處于這樣一個位置:
NSURLProtocol能攔截哪些網(wǎng)絡(luò)請求
NSURLProtocol能攔截所有基于URL Loading System的網(wǎng)絡(luò)請求。
可以攔截的網(wǎng)絡(luò)請求包括NSURLSession儒鹿,NSURLConnection以及UIWebVIew化撕。基于CFNetwork的網(wǎng)絡(luò)請求挺身,以及WKWebView的請求是無法攔截的『钏現(xiàn)在主流的iOS網(wǎng)絡(luò)庫,例如AFNetworking章钾,Alamofire等網(wǎng)絡(luò)庫都是基于NSURLSession或NSURLConnection的墙贱,所以這些網(wǎng)絡(luò)庫的網(wǎng)絡(luò)請求都可以被NSURLProtocol所攔截。還有一些年代比較久遠(yuǎn)的網(wǎng)絡(luò)庫贱傀,例如ASIHTTPRequest惨撇,MKNetwokit等網(wǎng)路庫都是基于CFNetwork的,所以這些網(wǎng)絡(luò)庫的網(wǎng)絡(luò)請求無法被NSURLProtocol攔截府寒。
NSURLProtocol的使用
自定義NSURLProtocol的子類YHCacheURLProtocol然后在發(fā)起請求前注冊
對于基于NSURLConnection或者使用[NSURLSession sharedSession]創(chuàng)建的網(wǎng)絡(luò)請求魁衙,調(diào)用registerClass方法即可
[NSURLProtocol registerClass:[YHCacheURLProtocol class]];
對于基于NSURLSession的網(wǎng)絡(luò)請求,需要通過配置NSURLSessionConfiguration對象的protocolClasses屬性株搔。
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.protocolClasses = @[[YHCacheURLProtocol class]];
攔截
在攔截到網(wǎng)絡(luò)請求后剖淀,NSURLProtocol會依次執(zhí)行下列方法:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
/*
(1).處理返回YES,不處理返回NO
(2).打標(biāo)簽纤房,已經(jīng)處理過的不在處理
*/
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSString *url = request.URL.absoluteString;
if ([url containsString:@".jpg"] || [url containsString:@".jpeg"] || [url containsString:@".png"]) {
//處理過的纵隔,放過
if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
return NO;
}
return YES;
}
return NO;
}
該方法會拿到request的對象,我們可以通過該方法的返回值來篩選request是否需要被NSURLProtocol做攔截處理炮姨。
這里我們需要緩存圖片的request 所以判斷url是否是圖片格式再進(jìn)行攔截
[NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]
該方法是判斷這個request是否處理過的捌刮。在后面處理request的時候會對request進(jìn)行標(biāo)記
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
在該方法中,我們可以對request進(jìn)行處理舒岸。例如修改頭部信息等绅作。最后返回一個處理后的request實例。如果需要處理 對request進(jìn)行mutableCopy然后進(jìn)行修改
這里我們只對WebView的圖片做緩存 所以對request不做修改
- (void)startLoading
- (void)startLoading
{
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
NSString *url = self.request.URL.absoluteString;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage *image = [[SDWebImageManager sharedManager].imageCache imageFromCacheForKey:url];
if (image) {
NSData *data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:SDImageFormatUndefined];
NSURLResponse *res = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:@"image/*;q=0.8" expectedContentLength:data.length textEncodingName:nil];
[self.client URLProtocol:self didReceiveResponse:res cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
} else {
//request處理過的放進(jìn)去
[NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
[[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:self.request.URL options:SDWebImageDownloaderHighPriority progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (error) {
[self.client URLProtocol:self didFailWithError:error];
} else {
[[SDWebImageManager sharedManager].imageCache storeImageDataToDisk:data forKey:url];
NSURLResponse *res = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:@"image/*;q=0.8" expectedContentLength:data.length textEncodingName:nil];
[self.client URLProtocol:self didReceiveResponse:res cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
}
}];
}
});
}
在這個方法里 處理我們攔截到的webview圖片加載的request
在這里判斷url對應(yīng)的圖片在本地是否有緩存蛾派,如果有 就直接調(diào)用
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
如果不存在緩存俄认,就去請求
/*!
@abstract Returns the NSURLProtocolClient of the receiver.
@result The NSURLProtocolClient of the receiver.
*/
@property (nullable, readonly, retain) id <NSURLProtocolClient> client;
client是一個遵守了NSURLProtocolClient協(xié)議的對象
NSURLProtocolClient協(xié)議主要用到下面幾個方法:
- (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;