iOS關於html緩存


方式一:截取請求正則、圖片緩存

實現webview緩存網頁內容難點在緩存圖片上。html代碼的緩存相對簡單,
具體實現思路是這樣的:
第1步、先獲取html頁面里所有圖片地址。
方法一:離線獲取獲取到html代碼。html代碼你可以把他理解成是一個很長的字符串。通過正則表達式把這個html頁面里的所有img標簽url。如果是相對url,就加上host。如果是絕對url,就直接下載。這樣這個頁面里的所有圖片路徑都拿到了。
方法一的獲取img標簽url的正則表達式:
NSString *urlPattern = @"<img[^>]+?src=[\"']?([^>'\"]+)[\"']?";

方法二:通過webview和js 本地程序的交換,獲取到html頁面所有圖片下載地址。
webview和本地程序交互的方法是_detailWebView stringByEvaluatingJavaScriptFromString。
這是方法二獲取圖片url的js代碼如下:

 
1
2
3
4
5
6
7
8
9
10
11
//獲取web里的所有的img url
- ( NSString  *)createImgArrayJavaScript{
     NSString  *js = @ "var imgArray = document.getElementsByTagName('img'); var imgstr = ''; function f(){ for(var i = 0; i < imgArray.length; i++){ imgstr += imgArray[i].src;imgstr += ';';} return imgstr; } f();" ;
     return  js;
}
 
//返回web img圖片的數量
- ( NSString  *)createGetImgNumJavaScript{
     NSString  *js = @ "var imgArray = document.getElementsByTagName('img');function f(){ var num=imgArray.length;return num;} f();" ;
     return  js;
}



第2步、把下載圖片到本地,把本地的圖片設置到html代碼中去顯示。
通過上面說的兩個方法,你可以獲取到圖片地址了並且能下載到本地了。那沒網絡的情況下怎么把這些圖片再設置到html頁面里呢?
方法:下載到本地的圖片命名一律使用圖片url的md5。因為url直接做不了圖片的名稱。 
下面這段代碼演示了下載圖片和設置本地圖片的全過程。代碼的邏輯是:有本地圖片,就顯示本地圖片,如果沒有則從網絡獲取。還有對應的js代碼。設置圖片是還判斷圖片的寬度,大於300時,就等比例縮小。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
- ( void )downLoadImageFromURL:( NSArray * )imageUrlArray
{
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
     dispatch_group_t group = dispatch_group_create();
     
     for  ( int  i = 0; i < imageUrlArray.count; i++)
     {
         NSString  *imageUrl = [imageUrlArray objectAtIndex:i];
         NSString  *key = [imageUrl MD5Hash];
         NSData  *data = [FTWCache objectForKey:key];
         NSURL  *imageURL = [ NSURL  URLWithString:imageUrl];
         NSString  *index = [ NSString  stringWithFormat:@ "%d" , i];
 
         if  (data) {
             [_detailWebView stringByEvaluatingJavaScriptFromString:[ self  createSetImageUrlJavaScript:index
                                                                                               imgUrl:key]];
             
         } else {
             dispatch_group_async(group, queue, ^{
              NSData  *data = [ NSData  dataWithContentsOfURL:imageURL];
              if  (data !=  nil ) {
                  [FTWCache setObject:data forKey:key];
                  dispatch_sync(dispatch_get_main_queue(), ^{
                      [_detailWebView stringByEvaluatingJavaScriptFromString:[ self  createSetImageUrlJavaScript:index
                                                                                                        imgUrl:key]];
                      
                     
                      DDLOG(@ "image i %d" ,i);
                  });
                   
              }
         });
         
         }
     }
     
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
         //這里是所有圖片下載完成后執行的操作。
     });
     
     dispatch_release(group);
}
 
//設置下載完成的圖片到web img
- ( NSString  *)createSetImageUrlJavaScript:( NSString  *) index imgUrl:( NSString  *) url{
     NSData  *imageData = [FTWCache objectForKey:url];
     UIImage   *image = [ UIImage  imageWithData:imageData];
     int  imageWidth = 300;
     int  imageHeight = image.size.height*300.0f/image.size.width;
     NSString  *js = [ NSString  stringWithFormat:@ "var imgArray = document.getElementsByTagName('img'); imgArray[%@].src=\"%@\"; imgArray[%@].width=\"%d\";imgArray[%@].height=\"%d\" ;"  , index, url, index,imageWidth,index,imageHeight];
     return  js;
}
 
完整的通過正則獲取html代碼中的所有圖片url的代碼如下:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- ( void ) getImageUrlArray:( NSString *) content
{
     DDLOG_CURRENT_METHOD;
     NSString  *urlPattern = @ "<img[^>]+?src=[\"']?([^>'\"]+)[\"']?" ;
     NSError  *error = [ NSError  new ];
     
     NSRegularExpression  *regex = [ NSRegularExpression  regularExpressionWithPattern:urlPattern options: NSRegularExpressionCaseInsensitive  error:&error ];
     
     //match 這塊內容非常強大
     NSUInteger  count =[regex numberOfMatchesInString:content options: NSRegularExpressionCaseInsensitive  range: NSMakeRange (0, [content length])]; //匹配到的次數
     if (count > 0){
         NSArray * matches = [regex matchesInString:content options: NSMatchingReportCompletion  range: NSMakeRange (0, [content length])];
         
         for  ( NSTextCheckingResult  *match in matches) {
             
             NSInteger  count = [match numberOfRanges]; //匹配項
             for ( NSInteger  index = 0;index < count;index++){
                 NSRange  halfRange = [match rangeAtIndex:index];
                 if  (index == 1) {
                     [listImage addObject:[content substringWithRange:halfRange]];
                 }
             }
         } //遍歷后可以看到三個range,1、為整體。2、為([\\w-]+\\.)匹配到的內容。3、(/?[\\w./?%&=-]*)匹配到的內容
     }
 
}
 

方式二:NSURLProtocol緩存

 

可能很多人使用過UIWebView,也考慮過如何在離線狀態下讀取緩存的問題,但是好像沒有一個標准的答案。有的人說可以,有的人說不行。通過這幾天的研究,我發現了一些問題,在這里希望各位給點意見。

另外,還要求助一下,有沒有人知道在ios上測試網頁加載速度的工具啊? 找了好久都沒看到。最好是免費的。
一.HTML5 , Manifest
最開始我的想法是使用HTML5中的離線存儲功能,也就是分析Manifest文件來存儲和更新部分資源文件。但是經過實踐發現,UIWebView根本不支持HTML5,他只實現了Webkit中頁面渲染的那一部分。所以要實現緩存必須要另辟蹊徑。

二.ASIHTTPRequest,ASIDownloadCache  和 ASIWebPageRequest
   首先我得說,這確實是個很好的框架,使用起來確實很方便,但是對於緩存這個問題,好像也跟第二點提到的效果差不多,加載速度沒有明顯的提升,離線模式下也無法加載。這是實現的代碼

  1. -(void)loadURL:(NSURL*)url  
  2. {  
  3.     ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];  
  4.     //ASIWebPageRequest *request= [ASIWebPageRequest requestWithURL:url];  
  5.     [request setDelegate:self];  
  6.     //[request setUrlReplacementMode:ASIReplaceExternalResourcesWithData];  
  7.     [request setDidFailSelector:@selector(webPageFetchFailed:)];  
  8.     [request setDidFinishSelector:@selector(webPageFetchSucceeded:)];  
  9.      //設置緩存  
  10.     [request setDownloadCache:[ASIDownloadCache sharedCache]];  
  11.     //[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];  
  12.     [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];  
  13.     [request setDownloadDestinationPath:[[ASIDownloadCache sharedCache]pathToStoreCachedResponseDataForRequest:request]];  
  14.      [request startAsynchronous];  
  15. }  
  16.   
  17.   
  18. - (void)webPageFetchFailed:(ASIHTTPRequest *)theRequest  
  19. {  
  20.     // Obviously you should handle the error properly...  
  21.     NSLog(@"%@",[theRequest error]);  
  22.     NSString *path = [[NSBundle mainBundle] pathForResource:@"error1.html" ofType:nil inDirectory:@"WebResources/Error"];  
  23.     NSURL  *url=[NSURL fileURLWithPath:path];    
  24.     [viewer loadRequest:[NSURLRequest requestWithURL:url]];  
  25. }  
  26.   
  27. - (void)webPageFetchSucceeded:(ASIHTTPRequest *)theRequest  
  28. {  
  29.     NSString *response = [NSString stringWithContentsOfFile:  
  30.                           [theRequest downloadDestinationPath] encoding:[theRequest responseEncoding] error:nil];  
  31.     // Note we're setting the baseURL to the url of the page we downloaded. This is important!  
  32.     [viewer loadHTMLString:response baseURL:[theRequest url]];  
  33.     //[viewer loadHTMLString:response baseURL:nil];  
  34. }  

 

三.NSURLCache

  盡管在官方的說明文檔里面說到NSURLCache和NSCachedURLResponse可以用於緩存,但經我測試好像僅僅只能用於加載本地某些資源文件(這里有一篇博客,原文是英文的,這是翻譯過來的
,而且還有大小的限制(好像根據iphone的版本不同而不同,最小是25KB吧),比如圖片和JS代碼, 而對於整體的頁面無法進行加載。而且經過測試也沒有感覺加載速度有明顯的提高,我用的緩存策略是NSURLRequestReturnCacheDataElseLoad(可能是沒有讀取本地的緩存文件?),離線模式下也無法加載(可能是baseURL的關系?)。
這找到一篇博客,一種新的解決思路,經過我測試,可以很好的實現緩存。
另外做一點引申,對於動態獲取數據的頁面,我們不需要緩存的那些請求,只要過濾掉就可以了。
先新建一個文件,把所有不需要緩存的請求的URL寫在一個文件里,就象HTML5的 Cache Manifest那樣。
然后需要使用緩存的時候讀取這個文件,並在重寫的- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request  這個方法內對請求進行判斷,如果是屬於這個文件內的,比如web service的請求就直接返回,其他的就繼續處理。

 
四.利用NSURLProtocol實現離線緩存(推薦 對於一般簡單緩存)
 

搜索解決方案的時候找到了Rob Napier 的博文:Drop-in offline caching for UIWebView (and NSURLProtocol) 文章介紹了使用NSURLProtocol實現UIWebView的離線緩存的簡單實現,你可以在github上下載這個demo的代碼。

rob認為無論是“MKNetworkKit”還是”AFCache”實現的緩存都過於復雜,而他想要的是一個簡單機制:

1、你使用了UIWebView指向來顯示一個有圖像嵌入的網站。
2、當你的設備online時,你有正常的緩存算法。
3、當你的設備offline時,你可以顯示頁面的最后一個版本。

這個demo里做了一個很簡單的測試:將cnn.com運行一次,然后再將網絡斷掉,去瀏覽這些數據。

現有解決方案:

Matt Gallagher 有一些有趣的想法,使用NSURLCache的子類來實現,但是Rob發現這是不可靠的,尤其是iOS5的HTTP緩存規則十分復雜,在許多情況下如果你不訪問服務器便不能獲知你緩存的數據是否有效。另外,一些必要的素材如果沒有被緩存,那么在離線時前期做的緩存工作就實效了。(輝:NSURLCache實現離線閱讀的一點小心得 我也曾討論了一些相關問題)

AFCache也被認為是一個很好的解決方案(輝:有時間我會對這個開源庫進行詳細評估,表面上看就是connection、NSURLCache、NSURLProtocol的綜合解決方案)。短時間內作者並沒有使測試通過,但是AFCache的作者也在文章后邊回復說,采納了Rob的想法,已經提交代碼到github上。

要點:
1、盡早注冊你的URLProtocol(application:didFinishLaunchingWithOptions:)。
2、NSURLProtocol是NSURLConnection的handler。NSURLConnection的每個請求都會去便利所有的Protocols,並詢問你能處理這個請求么(canInitWithRequest: )。如果這個Protocol返回YES,則第一個返回YES的Protocol會來處理這個connection。Protocols的遍歷是反向的,也就是最后注冊的Protocol會被優先判斷。
3、 當你的handler被選中了,connection就會調用–> initWithRequest:cachedResponse:client:,緊接着會調用–>startLoading。然后你需要負責回調:–>URLProtocol:didReceiveResponse:cacheStoragePolicy:,有些則會調用:–>URLProtocol:didLoadData:, 並且最終會調用–>URLProtocolDidFinishLoading:。你有沒有發現這些方法和NSURLConnection delegate的方法非常類似——這絕非偶然!
4、當online的情況下,RNCachingURLProtocol只是負責將請求轉發給一個新的NSURLConnection,並且拷貝一份結果給原來的connection。offline時, RNCachingURLProtocol就會從磁盤里載入先前的結果,並將這些數據發回給連接。整個過程只有區區200行代碼(不包含Reachability)。
5、這里還有一個有趣的問題,就是當RNCachingURLProtocol創建了一個新的NSURLConnection的,即新的connection也會去找一個handler。 如果RNCachingURLProtocol說可以處理,那么就死循環了。怎么解決呢?通過添加自定義HTTP Header(X-RNCache)來標記這個請求,告訴RNCachingURLProtocol不要再處理這個請求。
6、它可以響應所有的connection,所以你可能需要修改canInitWithRequest:來 選擇你要緩存的數據。

另外:並發請求或復雜網絡請求的緩存請使用MKNetworkKit(我們也在一個項目中使用了這個類庫,非常輕量快捷是ASI的很不錯的替代品)。

總結一下:
這項技術不是用來替代AFCache、MKNetworkKit的,他只是用來解決獨立的、簡單問題的(當然它也可以通過復雜實現來解決復雜問題)。 NSURLProtocol是非常強大的,Rob已經使用它來監聽網絡流量(如PandoraBoy中的幾個ProxyURLProtocol類)。它非常值得你將其添加到你的工具箱中。

實例代碼下載

#方式二還有某技術人員分享的代碼如下

//WebCachedData.h
#import <Foundation/Foundation.h>

static NSUInteger const kCacheExpireTime = 600;//緩存的時間 默認設置為600秒

@interface WebCachedData :NSObject <NSCoding>
@property (nonatomic,strong) NSData *data;
@property (nonatomic,strong) NSURLResponse *response;
@property (nonatomic,strong) NSURLRequest *redirectRequest;
@property (nonatomic,strong) NSDate *date;
@end

//WebCachedData.m
#import "WebCachedData.h"

static NSString *const kDataKey =@"data";
static NSString *const kResponseKey =@"response";
static NSString *const kRedirectRequestKey =@"redirectRequest";
static NSString *const kDateKey =@"date";

@implementation WebCachedData

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:[self data] forKey:kDataKey];
    [aCoder encodeObject:[self response] forKey:kResponseKey];
    [aCoder encodeObject:[self redirectRequest] forKey:kRedirectRequestKey];
    [aCoder encodeObject:[self date] forKey:kDateKey];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self !=nil) {
        [self setData:[aDecoder decodeObjectForKey:kDataKey]];
        [self setResponse:[aDecoder decodeObjectForKey:kResponseKey]];
        [self setRedirectRequest:[aDecoder decodeObjectForKey:kRedirectRequestKey]];
        [self setDate:[aDecoder decodeObjectForKey:kDateKey]];
    }
    
    return self;
}

@end


//CacheURLProtocol.h
#import <Foundation/Foundation.h>
#import "WebCachedData.h"

@interface CacheURLProtocol : NSURLProtocol

@end


//CacheURLProtocol.m
#import "CacheURLProtocol.h"
#import <CommonCrypto/CommonDigest.h>

static NSString *const kOurRecursiveRequestFlagProperty =@"COM.WEIMEITC.CACHE";
static NSString *const kSessionQueueName =@"WEIMEITC_SESSIONQUEUENAME";
static NSString *const kSessionDescription =@"WEIMEITC_SESSIONDESCRIPTION";


@interface CacheURLProtocol()<NSURLSessionDataDelegate>

@property (nonatomic,strong) NSURLSession                  *session;
@property (nonatomic,copy) NSURLSessionConfiguration       *configuration;
@property (nonatomic,strong) NSOperationQueue              *sessionQueue;
@property (nonatomic,strong) NSURLSessionDataTask          *task;
@property (nonatomic,strong) NSMutableData                 *data;
@property (nonatomic,strong) NSURLResponse                 *response;

- (void)appendData:(NSData *)newData;
@end




@implementation CacheURLProtocol

static NSObject *CacheURLProtocolIgnoreURLsMonitor;
static NSArray  *CacheURLProtocolIgnoreURLs;



+ (BOOL)registerProtocolWithIgnoreURLs:(NSArray*)ignores {
    [self unregisterCacheURLProtocol];
    [self setIgnoreURLs:ignores];
    return [[self class] registerClass:[self class]];
}


+ (void)unregisterCacheURLProtocol {
    [self setIgnoreURLs:nil];
    [[self class] unregisterClass:[self class]];
}

- (void)dealloc {
    [self.task cancel];
    
    [self setTask:nil];
    [self setSession:nil];
    [self setData:nil];
    [self setResponse:nil];
    [self setSessionQueue:nil];
    [self setConfiguration:nil];
    [[self class] setIgnoreURLs:nil];
}

+(void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        CacheURLProtocolIgnoreURLsMonitor = [NSObject new];
    });
}

#pragma mark - URLProtocol APIs
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    // 可以修改request對象
    return request;
}

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

+ (BOOL)canInitWithTask:(NSURLSessionTask *)task {
    return [self canInitWithURLRequest:task.currentRequest];
}

- (instancetype)initWithTask:(NSURLSessionTask *)task cachedResponse:(nullable NSCachedURLResponse *)cachedResponse client:(nullable id <NSURLProtocolClient>)client {
    
    self = [super initWithTask:task cachedResponse:cachedResponse client:client];
    if (self !=nil) {
        [self configProtocolParam];
    }
    return self;
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    return [self canInitWithURLRequest:request];
}

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client {
    
    self = [super initWithRequest:request cachedResponse:cachedResponse client:client];
    if (self !=nil) {
        [self configProtocolParam];
    }
    return self;
}

- (void)startLoading {
    
    WebCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]];
    
    // 這地方不能判斷cache.data字段,有可能是一個重定向的request
    if (cache) {
        // 本地有緩存
        NSData *data = [cache data];
        
        NSURLResponse *response = [cache response];
        NSURLRequest *redirectRequest = [cache redirectRequest];
        NSDate *date = [cache date];
        if ([self expireCacheData:date]) {
            // 數據過期
            NSLog(@"request Data-expire!");
            NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy];
            [[self class] setProperty:@(YES) forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest];
            self.task = [self.session dataTaskWithRequest:recursiveRequest];
            [self.task resume];
            
        } else {
            if (redirectRequest) {
                [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response];
            } else {
                
                if (data) {
                    NSLog(@"cached Data!");
                    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
                    [[self client] URLProtocol:self didLoadData:data];
                    [[self client] URLProtocolDidFinishLoading:self];
                } else {
                    // 本地沒有緩存上data
                    NSLog(@"request Data-uncached data!");
                    NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy];
                    [[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest];
                    self.task = [self.session dataTaskWithRequest:recursiveRequest];
                    [self.task resume];
                }
            }
        }
        
    } else {
        
        // 本地無緩存
        NSLog(@"request Data-no data!");
        NSMutableURLRequest *recursiveRequest = [[self request] mutableCopy];
        [[self class] setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:recursiveRequest];
        self.task = [self.session dataTaskWithRequest:recursiveRequest];
        [self.task resume];
    }
}

- (void)stopLoading {
    [self.task cancel];
    
    [self setTask:nil];
    [self setData:nil];
    [self setResponse:nil];
}

#pragma mark - NSURLSession delegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler {
    
    if (response !=nil) {
        NSMutableURLRequest *redirectableRequest = [newRequest mutableCopy];
        [[self class] removePropertyForKey:kOurRecursiveRequestFlagProperty inRequest:redirectableRequest];
        
        NSString *cachePath = [self cachePathForRequest:[self request]];
        WebCachedData *cache = [[WebCachedData alloc] init];
        [cache setResponse:response];
        [cache setData:[self data]];
        [cache setDate:[NSDate date]];
        [cache setRedirectRequest:redirectableRequest];
        [NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
        
        [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response];
        
        [self.task cancel];
        [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
        
        completionHandler(redirectableRequest);
    } else {
        completionHandler(newRequest);
    }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void(^)(NSURLSessionResponseDisposition))completionHandler {
    
    [self setResponse:response];
    [self setData:nil];
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    
    [[self client] URLProtocol:self didLoadData:data];
    [self appendData:data];
}


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)dataTask didCompleteWithError:(NSError *)error {
    
    if (error ) {
        [self.client URLProtocol:self didFailWithError:error];
    } else {
        NSString *cachePath = [self cachePathForRequest:[self request]];
        WebCachedData *cache = [[WebCachedData alloc] init];
        [cache setResponse:[self response]];
        [cache setData:[self data]];
        [cache setDate:[NSDate date]];
        [NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
        
        [[self client] URLProtocolDidFinishLoading:self];
    }
}

#pragma mark - private APIs

+ (NSArray *)ignoreURLs {
    NSArray *iURLs;
    @synchronized(CacheURLProtocolIgnoreURLsMonitor) {
        iURLs = CacheURLProtocolIgnoreURLs;
    }
    return iURLs;
}

+ (void)setIgnoreURLs:(NSArray *)iURLs {
    @synchronized(CacheURLProtocolIgnoreURLsMonitor) {
        CacheURLProtocolIgnoreURLs = iURLs;
    }
}

+ (BOOL)canInitWithURLRequest:(NSURLRequest*)request {
    
    // 過濾掉不需要走URLProtocol
    NSArray *ignores = [self ignoreURLs];
    for (NSString *url in ignores) {
        if ([[request.URL absoluteString] hasPrefix:url]) {
            return NO;
        }
    }
    
    // 如果是startLoading里發起的request忽略掉,避免死循環
    BOOL recurisve = [self propertyForKey:kOurRecursiveRequestFlagProperty inRequest:request] == nil;
    
    // 沒有標識位kOurRecursiveRequestFlagProperty的並且是以http開的scheme都走代理;
    if (recurisve && [[request.URL scheme] hasPrefix:@"http"]) {
        return YES;
    }
    
    return NO;
}

- (void)configProtocolParam {
    NSURLSessionConfiguration *config = [[NSURLSessionConfiguration defaultSessionConfiguration] copy];
    [config setProtocolClasses:@[ [self class] ]];
    [self setConfiguration:config];
    
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    [q setMaxConcurrentOperationCount:1];
    [q setName:kSessionQueueName];
    [self setSessionQueue:q];
    
    NSURLSession *s = [NSURLSession sessionWithConfiguration:_configuration delegate:self delegateQueue:_sessionQueue];
    s.sessionDescription =kSessionDescription;
    [self setSession:s];
}

- (NSString*)md5Encode:(NSString*)srcString {
    const char *cStr = [srcString UTF8String];
    unsigned char result[16];
    CC_MD5( cStr, (unsigned int)strlen(cStr), result);
    
    NSString *formatString =@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
    
    return [NSString stringWithFormat:formatString,
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]];
}

- (NSString *)cachePathForRequest:(NSURLRequest *)aRequest {
    NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES)lastObject];
    NSString *fileName = [self md5Encode:[[aRequest URL]absoluteString]];
    return [cachesPath stringByAppendingPathComponent:fileName];
}

- (void)appendData:(NSData *)newData {
    if ([self data] == nil) {
        self.data = [[NSMutableData alloc] initWithCapacity:0];
    }
    
    if (newData) {
        [[self data] appendData:newData];
    }
}

- (BOOL)expireCacheData:(NSDate *)date {
    
    if (!date) {
        return YES;
    }
    
    NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:date];
    BOOL bRet = timeInterval <kCacheExpireTime;
    if (!bRet) {
        // 過期刪除緩存
        NSString *filename = [self cachePathForRequest:[self request]];
        NSFileManager *defaultManager = [NSFileManager defaultManager];
        if ([defaultManager isDeletableFileAtPath:filename]) {
            [defaultManager removeItemAtPath:filename error:nil];
        }
    }
    
    return !bRet;
}

@end
他山之石
 
鏈接:
 
iOS開發之--- NSURLProtocol(截取request請求)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM