4、下面我们设计缓存项下载成功和失败的两个委托方法:

  1. @protocol CacheItemDelegate <NSObject>  

  2. //下载成功执行该方法  

  3. - (void) cacheItemDelegateSucceeded  

  4.    :(CacheItem *)paramSender  

  5.    withRemoteURL:(NSURL *)paramRemoteURL  

  6.    withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData;  

  7.    

  8. //下载失败执行该方法  

  9. - (void) cacheItemDelegateFailed  

  10.    :(CacheItem *)paramSender  

  11.    remoteURL:(NSURL *)paramRemoteURL  

  12.    withError:(NSError *)paramError;  

  13.    

  14. @end

复制代码


        当我们下载成功的时候,修改缓存字典中的下载时间,表示已经下载完成,而且需要将请求的资源数据缓存到本地:

  1. //缓存项的委托方法  

  2. - (void) cacheItemDelegateSucceeded:(CacheItem *)paramSender  

  3.           withRemoteURL:(NSURL *)paramRemoteURL  

  4.          withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData{  

  5.      

  6.    //从缓存字典中获取该缓存项的相关数据  

  7.    NSMutableDictionary *dictionary =   

  8.    [self.cacheDictionary objectForKey:[paramRemoteURL absoluteString]];  

  9.    //取当前时间  

  10.    NSDate *now = [NSDate date];  

  11.    //获取有效时间  

  12.    NSNumber *expiresInSeconds = [dictionary   

  13.                                  objectForKey:CachedKeyExpiresInSeconds];  

  14.    //转换成NSTimeInterval  

  15.    NSTimeInterval expirySeconds = [expiresInSeconds floatValue];  

  16.    //修改字典中缓存项的下载结束时间  

  17.    [dictionary setObject:[NSDate date]  

  18.                   forKey:CachedKeyDownloadEndDate];  

  19.    //修改字典中缓存项的缓存过期时间  

  20.    [dictionary setObject:[now dateByAddingTimeInterval:expirySeconds]  

  21.                   forKey:CachedKeyExpiryDate];  

  22.    //保存缓存字典  

  23.    [self saveCacheDictionary];  

  24.      

  25.    NSString *localURL = [dictionary objectForKey:CachedKeyLocalURL];  

  26.      

  27.    /* 将下载的数据保持到磁盘 */  

  28.    if ([paramAboutToBeReleasedData writeToFile:localURL  

  29.                                     atomically:YES] == YES){  

  30.      NSLog(@"缓存文件到磁盘成功.");  

  31.    } else{  

  32.      NSLog(@"缓存文件到磁盘失败.");  

  33.    }  

  34.    //执行缓存管理的委托方法  

  35.    [self.delegate   

  36.     cachedDownloadManagerSucceeded:self  

  37.     remoteURL:paramRemoteURL  

  38.     localURL:[NSURL URLWithString:localURL]  

  39.     aboutToBeReleasedData:paramAboutToBeReleasedData  

  40.     isCachedData:NO];  

  41.      

  42.      

  43. }

复制代码


        如果下载失败我们需要从缓存字典中移除改缓存项:

  1. //缓存项失败失败的委托方法  

  2. - (void) cacheItemDelegateFailed:(CacheItem *)paramSender  

  3.                         remoteURL:(NSURL *)paramRemoteURL  

  4.                         withError:(NSError *)paramError{  

  5.      

  6.    /* 从缓存字典中移除缓存项,并发送一个委托 */  

  7.      

  8.    if (self.delegate != nil){  

  9.        

  10.      NSMutableDictionary *dictionary =   

  11.      [self.cacheDictionary   

  12.       objectForKey:[paramRemoteURL absoluteString]];  

  13.        

  14.      NSString *localURL = [dictionary   

  15.                            objectForKey:CachedKeyLocalURL];  

  16.        

  17.      [self.delegate  

  18.       cachedDownloadManagerFailed:self  

  19.       remoteURL:paramRemoteURL  

  20.       localURL:[NSURL URLWithString:localURL]  

  21.       withError:paramError];  

  22.    }  

  23.      

  24.    [self.cacheDictionary   

  25.     removeObjectForKey:[paramRemoteURL absoluteString]];  

  26.      

  27. }

复制代码


5、加载缓存字典的时候,我们可以将没有下载完成的文件移除:

  1. //初始化缓存字典  

  2.    NSString *documentsDirectory =   

  3.    [self documentsDirectoryWithTrailingSlash:YES];  

  4.    //生产缓存字典的路径  

  5.    cacheDictionaryPath =   

  6.    [[documentsDirectory   

  7.      stringByAppendingString:@"CachedDownloads.dic"] retain];  

  8.    //创建一个NSFileManager实例  

  9.    NSFileManager *fileManager = [[NSFileManager alloc] init];  

  10.    //判断是否存在缓存字典的数据  

  11.    if ([fileManager   

  12.         fileExistsAtPath:self.cacheDictionaryPath] == YES){  

  13.        NSLog(self.cacheDictionaryPath);  

  14.      //加载缓存字典中的数据  

  15.      NSMutableDictionary *dictionary =   

  16.      [[NSMutableDictionary alloc]   

  17.       initWithContentsOfFile:self.cacheDictionaryPath];  

  18.        

  19.      cacheDictionary = [dictionary mutableCopy];  

  20.        

  21.      [dictionary release];  

  22.        

  23.      //移除没有下载完成的缓存数据  

  24.      [self removeCorruptedCachedItems];  

  25.        

  26.    } else {  

  27.      //创建一个新的缓存字典  

  28.      NSMutableDictionary *dictionary =   

  29.      [[NSMutableDictionary alloc] init];  

  30.        

  31.      cacheDictionary = [dictionary mutableCopy];  

  32.        

  33.      [dictionary release];  

  34.        

  35.    }

复制代码


       这样就基本上完成了我们需要的功能,下面看看我们如何使用我们设计的缓存功能。

例子场景:


       我们用一个UIWebView来显示stackoverflow这个网站,我们在这个网站的内容缓存到本地20秒,如果在20秒内用户去请求该网站,则从本地文件中获取内容,否则过了20秒,则重新获取数据,并缓存到本地。


       在界面上拖放一个button和一个webview控件,如下图。

<ignore_js_op> 本地缓存机制之二_ 本地缓存机制之二

 



       这样我们可以很方便使用前面定义好的类。我们在viewDidLoad 中实例化一个CachedDownloadManager,并设置它的委托为self。当下载完成的时候,执行CachedDownloadManager的下载成功的委托方法。

  1. - (void)viewDidLoad { [super viewDidLoad]; [self setTitle:@"本地缓存测试"]; CachedDownloadManager *newManager =[[CachedDownloadManager alloc] init]; self.downloadManager = newManager; [newManager release]; [self.downloadManager setDelegate:self]; }

复制代码


       在button的点击事件中加入下面代码,请求stackoverflow :

  1. static NSString *url = @"http://stackoverflow.com"; [self.downloadManager download:url urlMustExpireInSeconds:20.0f updateExpiryDateIfInCache:YES];

复制代码


       上面的代码表示将这个stackoverflow的缓存事件设置为20s,并且如果在20s内有相同的请求,则从本地获取stackoverflow的内 容数据。updateExpiryDateIfInCache设置为yes表示:在此请求的时候,缓存时间又更新为20s,类似我们的session。如 果设置成no,则第一次请求20s之后,该缓存就过期。


       请求完成之后会执行CachedDownloadManager的委托方法。我们将数据展示在uiwebview中,代码如下:

  1. - (void) cachedDownloadManagerSucceeded:(CachedDownloadManager *)paramSender remoteURL:(NSURL *)paramRemoteURL localURL:(NSURL *)paramLocalURL aboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData isCachedData:(BOOL)paramIsCachedData{ [webview loadData:paramAboutToBeReleasedData MIMEType:@"text/html"textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@"http://stackoverflow.com"]]; }

复制代码


       这样我们就实现了20s的缓存。

 

效果:


       第一次点击测试按钮:

<ignore_js_op> 本地缓存机制之二_ 本地缓存机制之二_02

 



       20s内点击按钮,程序就从本地获取数据,比较快速的就显示出该网页了。



总结:


       本文通过代码和实例设计了一个iPhone应用程序本地缓存的方案。当然这个方案不是最好的,如果你有更好的思路,欢迎告诉我。