首页 > 代码库 > 本地缓存机制之一

本地缓存机制之一

 在手机应用程序开发中,为了减少与服务端的交互次数,加快用户的响应速度,一般都会在iOS设备中加一个缓存的机制,前面一篇文章介绍了iOS设备的内存缓存,这篇文章将设计一个本地缓存的机制。

 

功能需求

 

       这个缓存机制满足下面这些功能。

 

1、可以将数据缓存到本地磁盘。

 

2、可以判断一个资源是否已经被缓存。如果已经被缓存,在请求相同的资源,先到本地磁盘搜索。

 

3、可以判断文件缓存什么时候过期。这里为了简单起见这里,我们在请求url资源的时候,给每次请求的文件设定一个过期的时间。

 

4、可以实现:如果文件已经被缓存,而且没有过期,这将本地的数据返回,否则重新请求url。

 

5、可以实现:如果文件下载不成功或者下载没有完成,下次打开程序的时候,移除这些没有成功或者没有下载完成的文件。

 

6、可以实现:同时请求或者下载多个资源。

 

设计实现:

 

1、设计一个CacheItem类,用来请求一个web连接,它的一个实例表示一个缓存项。这个CacheItem类,需要一个url创建一个NSURLConnection,去请求web资源。使用CacheItem类主要用来请求web资源。

  1. /* ---------缓存项-------------- */  
  2.    
  3. @interface CacheItem : NSObject {  
  4. @public  
  5.    id<CacheItemDelegate> delegate;  
  6.      //web地址  
  7.    NSString              *remoteURL;  
  8. @private  
  9.      //是否正在下载  
  10.    BOOL                  isDownloading;  
  11.         //NSMutableData对象  
  12.    NSMutableData         *connectionData;  
  13.      //NSURLConnection对象  
  14.    NSURLConnection       *connection;  
  15. }  
  16.    
  17. /* -------------------------- */  
  18.    
  19. @property (nonatomic, retain) id<CacheItemDelegate> delegate;  
  20. @property (nonatomic, retain) NSString  *remoteURL;  
  21. @property (nonatomic, assign) BOOL      isDownloading;  
  22. @property (nonatomic, retain) NSMutableData *connectionData;  
  23. @property (nonatomic, retain) NSURLConnection *connection;  
  24.    
  25. /* ----------开始下载方法----------- */  
  26.    
  27. - (BOOL) startDownloadingURL:(NSString *)paramRemoteURL;  
  28.    
  29. @end
复制代码



2、在NSURLConnection开始请求之前,调用CachedDownloadManager类,来搜索和管理本地的缓存文件。将缓存文件的情况保存到一个字典类中。这个字典设计如下:

  1. {  
  2.    "http://www.cnn.com" =     {  
  3.      DownloadEndDate = "2011-08-02 07:51:57 +0100";  
  4.      DownloadStartDate = "2011-08-02 07:51:55 +0100";  
  5.      ExpiresInSeconds = 20;  
  6.      ExpiryDate = "2011-08-02 07:52:17 +0100";  
  7.      LocalURL = "/var/mobile/Applications/ApplicationID/Documents/  
  8.                  httpwww.cnn.com.cache";  
  9.    };  
  10.    "http://www.baidu.com" =     {  
  11.      DownloadEndDate = "2011-08-02 07:51:49 +0100";  
  12.      DownloadStartDate = "2011-08-02 07:51:44 +0100";  
  13.      ExpiresInSeconds = 20;  
  14.      ExpiryDate = "2011-08-02 07:52:09 +0100";  
  15.      LocalURL = "/var/mobile/Applications/ApplicationID/Documents/  
  16.                  httpwww.oreilly.com.cache";  
  17.    };  
  18. }
复制代码


       上面这个字典里面嵌套了字典。里面那层字典表示一个缓存项的缓存信息:下载结束时间、下载开始时间、缓存有效时间、缓存过期时间、缓存到本地的路径。

       下面看下CachedDownloadManager类。用它来实现和封装我们的缓存策略。

  1. /* -----------CachedDownloadManager-------------- */  
  2.    
  3. @interface CachedDownloadManager : NSObject   
  4.                                     <CacheItemDelegate> {  
  5. @public  
  6.    id<CachedDownloadManagerDelegate>  delegate;  
  7. @private  
  8. //记录缓存数据的字典  
  9.    NSMutableDictionary                *cacheDictionary;  
  10.                                         //缓存的路径  
  11.    NSString                           *cacheDictionaryPath;  
  12. }  
  13.    
  14.    
  15. @property (nonatomic, assign)   
  16. id<CachedDownloadManagerDelegate> delegate;  
  17.    
  18. @property (nonatomic, copy)   
  19. NSMutableDictionary *cacheDictionary;  
  20.    
  21. @property (nonatomic, retain)   
  22. NSString *cacheDictionaryPath;  
  23.    
  24.    
  25. /* 保持缓存字典 */  
  26.    
  27. - (BOOL) saveCacheDictionary;  
  28.    
  29. /* 公有方法:下载 */  
  30.    
  31. - (BOOL)         download:(NSString *)paramURLAsString  
  32.     urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds  
  33. updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache;  
  34.    
  35. /* -------------------------- */  
  36.    
  37. @end
复制代码

 

       从上面代码可以看出,这个管理缓存的类中,有一个缓存字典:cacheDictionary,用来表示所有资源的缓存情况;cacheDictionaryPath用来表示缓存的路径;saveCacheDictionary用来将缓存字典归档到本地文件中。download:urlMustExpireInSeconds:updateExpiryDateIfInCache是一个公共接口,通过传递url、缓存过期时间、是否更新缓存过期时间三个参数来方便的使用,实现我们的缓存策略。

 

3、如果这个文件已经被下载,而且没有过期,则从本地获取文件的数据。如果文件已经过期,则重新下载。我们通过download:urlMustExpireInSeconds:updateExpiryDateIfInCache方法来实现,主要看这个方法的代码:

  1. /* ---------下载-------------- */  
  2.    
  3. - (BOOL)         download:(NSString *)paramURLAsString  
  4.     urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds  
  5. updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache{  
  6.      
  7.    BOOL result = NO;  
  8.      
  9.    if (self.cacheDictionary == nil ||  
  10.        [paramURLAsString length] == 0){  
  11.      return(NO);  
  12.    }  
  13.      
  14.    paramURLAsString = [paramURLAsString lowercaseString];  
  15.    //根据url,从字典中获取缓存项的相关数据  
  16.    NSMutableDictionary *itemDictionary =   
  17.    [self.cacheDictionary objectForKey:paramURLAsString];  
  18.      
  19.    /* 使用下面这些变量帮助我们理解缓存逻辑 */  
  20.      //文件是否已经被缓存  
  21.    BOOL    fileHasBeenCached = NO;  
  22.      //缓存是否过期  
  23.    BOOL    cachedFileHasExpired = NO;  
  24.      //缓存文件是否存在  
  25.    BOOL    cachedFileExists = NO;  
  26.      //缓存文件能否被加载  
  27.    BOOL    cachedFileDataCanBeLoaded = NO;  
  28.      //缓存文件数据  
  29.    NSData  *cachedFileData = http://www.mamicode.com/nil;
  30.      //缓存文件是否完全下载  
  31.    BOOL    cachedFileIsFullyDownloaded = NO;  
  32.      //缓存文件是否已经下载  
  33.    BOOL    cachedFileIsBeingDownloaded = NO;  
  34.    //过期时间  
  35.    NSDate    *expiryDate = nil;  
  36.      //下载结束时间  
  37.    NSDate    *downloadEndDate = nil;  
  38.      //下载开始时间  
  39.    NSDate    *downloadStartDate = nil;  
  40.      //本地缓存路径  
  41.    NSString  *localURL = nil;  
  42.      //有效时间  
  43.    NSNumber  *expiresInSeconds = nil;  
  44.    NSDate    *now = [NSDate date];  
  45.      
  46.    if (itemDictionary != nil){  
  47.      fileHasBeenCached = YES;  
  48.    }  
  49.    //如果文件已经被缓存,则从缓存项相关数据中获取相关的值  
  50.    if (fileHasBeenCached == YES){  
  51.        
  52.      expiryDate = [itemDictionary   
  53.                    objectForKey:CachedKeyExpiryDate];  
  54.        
  55.      downloadEndDate = [itemDictionary  
  56.                         objectForKey:CachedKeyDownloadEndDate];  
  57.        
  58.      downloadStartDate = [itemDictionary  
  59.                           objectForKey:CachedKeyDownloadStartDate];  
  60.        
  61.      localURL = [itemDictionary  
  62.                  objectForKey:CachedKeyLocalURL];  
  63.        
  64.      expiresInSeconds = [itemDictionary  
  65.                          objectForKey:CachedKeyExpiresInSeconds];  
  66.      //如果下载开始和结束时间不为空,表示文件全部被下载  
  67.      if (downloadEndDate != nil &&   
  68.          downloadStartDate != nil){  
  69.        cachedFileIsFullyDownloaded = YES;  
  70.      }  
  71.        
  72.      /* 如果expiresInSeconds不为空,downloadEndDate为空,表示文件已经正在下载 */  
  73.      if (expiresInSeconds != nil &&  
  74.          downloadEndDate == nil){  
  75.        cachedFileIsBeingDownloaded = YES;  
  76.      }  
  77.        
  78.      /* 判断缓存是否过期 */  
  79.      if (expiryDate != nil &&  
  80.          [now timeIntervalSinceDate:expiryDate] > 0.0){  
  81.        cachedFileHasExpired = YES;  
  82.      }  
  83.        
  84.      if (cachedFileHasExpired == NO){  
  85.        /* 如果缓存文件没有过期,加载缓存文件,并且更新过期时间 */  
  86.        NSFileManager *fileManager = [[NSFileManager alloc] init];  
  87.          
  88.        if ([fileManager fileExistsAtPath:localURL] == YES){  
  89.          cachedFileExists = YES;  
  90.          cachedFileData = http://www.mamicode.com/[NSData >
  91.          if (cachedFileData != nil){  
  92.            cachedFileDataCanBeLoaded = YES;  
  93.          } /* if (cachedFileData != nil){ */  
  94.        } /* if ([fileManager fileExistsAtPath:localURL] == YES){ */  
  95.          
  96.        [fileManager release];  
  97.          
  98.        /* 更新缓存时间 */  
  99.          
  100.        if (paramUpdateExpiryDateIfInCache == YES){  
  101.            
  102.          NSDate *newExpiryDate =   
  103.          [NSDate dateWithTimeIntervalSinceNow:  
  104.           paramURLMustExpireInSeconds];  
  105.            
  106.          NSLog(@"Updating the expiry date from %@ to %@.",   
  107.                expiryDate,   
  108.                newExpiryDate);  
  109.            
  110.          [itemDictionary setObject:newExpiryDate  
  111.                             forKey:CachedKeyExpiryDate];  
  112.            
  113.          NSNumber *expires =   
  114.          [NSNumber numberWithFloat:paramURLMustExpireInSeconds];  
  115.            
  116.          [itemDictionary setObject:expires  
  117.                             forKey:CachedKeyExpiresInSeconds];  
  118.        }  
  119.          
  120.      } /* if (cachedFileHasExpired == NO){ */  
  121.        
  122.    }  
  123.      
  124.    if (cachedFileIsBeingDownloaded == YES){  
  125.      NSLog(@"这个文件已经正在下载...");  
  126.      return(YES);  
  127.    }  
  128.      
  129.    if (fileHasBeenCached == YES){  
  130.        
  131.      if (cachedFileHasExpired == NO &&  
  132.          cachedFileExists == YES &&  
  133.          cachedFileDataCanBeLoaded == YES &&  
  134.          [cachedFileData length] > 0 &&  
  135.          cachedFileIsFullyDownloaded == YES){  
  136.          
  137.        /* 如果文件有缓存而且没有过期 */  
  138.          
  139.        NSLog(@"文件有缓存而且没有过期.");  
  140.          
  141.        [self.delegate   
  142.         cachedDownloadManagerSucceeded:self  
  143.         remoteURL:[NSURL URLWithString:paramURLAsString]  
  144.         localURL:[NSURL URLWithString:localURL]  
  145.         aboutToBeReleasedData:cachedFileData  
  146.         isCachedData:YES];  
  147.          
  148.        return(YES);  
  149.          
  150.      } else {  
  151.        /* 如果文件没有被缓存,获取缓存失败 */  
  152.        NSLog(@"文件没有缓存.");  
  153.        [self.cacheDictionary removeObjectForKey:paramURLAsString];  
  154.        [self saveCacheDictionary];  
  155.      } /* if (cachedFileHasExpired == NO && */  
  156.        
  157.    } /* if (fileHasBeenCached == YES){ */  
  158.      
  159.    /* 去下载文件 */  
  160.      
  161.      
  162.    NSNumber *expires =   
  163.    [NSNumber numberWithFloat:paramURLMustExpireInSeconds];  
  164.      
  165.    NSMutableDictionary *newDictionary =   
  166.    [[[NSMutableDictionary alloc] init] autorelease];  
  167.      
  168.    [newDictionary setObject:expires   
  169.                      forKey:CachedKeyExpiresInSeconds];  
  170.      
  171.      
  172.    localURL = [paramURLAsString  
  173.                stringByAddingPercentEscapesUsingEncoding:  
  174.                NSUTF8StringEncoding];  
  175.      
  176.    localURL = [localURL stringByReplacingOccurrencesOfString:@"://"  
  177.                                                   withString:@""];  
  178.      
  179.    localURL = [localURL stringByReplacingOccurrencesOfString:@"/"  
  180.                                                   withString:@"{1}quot;];  
  181.      
  182.    localURL = [localURL stringByAppendingPathExtension:@"cache"];  
  183.      
  184.    NSString *documentsDirectory =   
  185.    [self documentsDirectoryWithTrailingSlash:NO];  
  186.      
  187.    localURL = [documentsDirectory   
  188.                stringByAppendingPathComponent:localURL];  
  189.      
  190.    [newDictionary setObject:localURL  
  191.                      forKey:CachedKeyLocalURL];  
  192.      
  193.    [newDictionary setObject:now  
  194.                      forKey:CachedKeyDownloadStartDate];  
  195.      
  196.    [self.cacheDictionary setObject:newDictionary  
  197.                             forKey:paramURLAsString];  
  198.      
  199.    [self saveCacheDictionary];  
  200.      
  201.    CacheItem *item = [[[CacheItem alloc] init] autorelease];  
  202.    [item setDelegate:self];  
  203.    [item startDownloadingURL:paramURLAsString];  
  204.      
  205.    return(result);  
  206.      
  207. }
文章来源:http://www.cnblogs.com/pengyingh/articles/2343039.html