功能需求

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

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

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

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

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

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

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

设计实现:

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

/* ---------缓存项-------------- */

@interface CacheItem : NSObject {
@public
  id<CacheItemDelegate> delegate;
    //web地址
  NSString              *remoteURL;
@private
    //是否正在下载
  BOOL                  isDownloading;
       //NSMutableData对象
  NSMutableData         *connectionData;
    //NSURLConnection对象
  NSURLConnection       *connection;
}

/* -------------------------- */

@property (nonatomic, assign) id<CacheItemDelegate> delegate;
@property (nonatomic, retain) NSString  *remoteURL;
@property (nonatomic, assign) BOOL      isDownloading;
@property (nonatomic, retain) NSMutableData *connectionData;
@property (nonatomic, retain) NSURLConnection *connection;

/* ----------开始下载方法----------- */

- (BOOL) startDownloadingURL:(NSString *)paramRemoteURL;

@end

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

{
  "http://www.cnn.com" =     {
    DownloadEndDate = "2011-08-02 07:51:57 +0100";
    DownloadStartDate = "2011-08-02 07:51:55 +0100";
    ExpiresInSeconds = 20;
    ExpiryDate = "2011-08-02 07:52:17 +0100";
    LocalURL = "/var/mobile/Applications/ApplicationID/Documents/
                httpwww.cnn.com.cache";
  };
  "http://www.baidu.com" =     {
    DownloadEndDate = "2011-08-02 07:51:49 +0100";
    DownloadStartDate = "2011-08-02 07:51:44 +0100";
    ExpiresInSeconds = 20;
    ExpiryDate = "2011-08-02 07:52:09 +0100";
    LocalURL = "/var/mobile/Applications/ApplicationID/Documents/
                httpwww.oreilly.com.cache";
  };
}

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

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

/* -----------CachedDownloadManager-------------- */

@interface CachedDownloadManager : NSObject 
                                   <CacheItemDelegate> {
@public
  id<CachedDownloadManagerDelegate>  delegate;
@private
//记录缓存数据的字典
  NSMutableDictionary                *cacheDictionary;
                                       //缓存的路径
  NSString                           *cacheDictionaryPath;
}

@property (nonatomic, assign) 
id<CachedDownloadManagerDelegate> delegate;

@property (nonatomic, copy) 
NSMutableDictionary *cacheDictionary;

@property (nonatomic, retain) 
NSString *cacheDictionaryPath;

/* 保持缓存字典 */

- (BOOL) saveCacheDictionary;

/* 公有方法:下载 */

- (BOOL)         download:(NSString *)paramURLAsString
   urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds
updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache;

/* -------------------------- */

@end

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

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

/* ---------下载-------------- */

- (BOOL)         download:(NSString *)paramURLAsString
   urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds
updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache{
  
  BOOL result = NO;
  
  if (self.cacheDictionary == nil ||
      [paramURLAsString length] == 0){
    return(NO);
  }
  
  paramURLAsString = [paramURLAsString lowercaseString];
  //根据url,从字典中获取缓存项的相关数据
  NSMutableDictionary *itemDictionary = 
  [self.cacheDictionary objectForKey:paramURLAsString];
  
  /* 使用下面这些变量帮助我们理解缓存逻辑 */
    //文件是否已经被缓存
  BOOL    fileHasBeenCached = NO;
    //缓存是否过期
  BOOL    cachedFileHasExpired = NO;
    //缓存文件是否存在
  BOOL    cachedFileExists = NO;
    //缓存文件能否被加载
  BOOL    cachedFileDataCanBeLoaded = NO;
    //缓存文件数据
  NSData  *cachedFileData = nil;
    //缓存文件是否完全下载
  BOOL    cachedFileIsFullyDownloaded = NO;
    //缓存文件是否已经下载
  BOOL    cachedFileIsBeingDownloaded = NO;
  //过期时间
  NSDate    *expiryDate = nil;
    //下载结束时间
  NSDate    *downloadEndDate = nil;
    //下载开始时间
  NSDate    *downloadStartDate = nil;
    //本地缓存路径
  NSString  *localURL = nil;
    //有效时间
  NSNumber  *expiresInSeconds = nil;
  NSDate    *now = [NSDate date];
  
  if (itemDictionary != nil){
    fileHasBeenCached = YES;
  }
  //如果文件已经被缓存,则从缓存项相关数据中获取相关的值
  if (fileHasBeenCached == YES){
    
    expiryDate = [itemDictionary 
                  objectForKey:CachedKeyExpiryDate];
    
    downloadEndDate = [itemDictionary
                       objectForKey:CachedKeyDownloadEndDate];
    
    downloadStartDate = [itemDictionary
                         objectForKey:CachedKeyDownloadStartDate];
    
    localURL = [itemDictionary
                objectForKey:CachedKeyLocalURL];
    
    expiresInSeconds = [itemDictionary
                        objectForKey:CachedKeyExpiresInSeconds];
    //如果下载开始和结束时间不为空,表示文件全部被下载
    if (downloadEndDate != nil && 
        downloadStartDate != nil){
      cachedFileIsFullyDownloaded = YES;
    }
    
    /* 如果expiresInSeconds不为空,downloadEndDate为空,表示文件已经正在下载 */
    if (expiresInSeconds != nil &&
        downloadEndDate == nil){
      cachedFileIsBeingDownloaded = YES;
    }
    
    /* 判断缓存是否过期 */
    if (expiryDate != nil &&
        [now timeIntervalSinceDate:expiryDate] > 0.0){
      cachedFileHasExpired = YES;
    }
    
    if (cachedFileHasExpired == NO){
      /* 如果缓存文件没有过期,加载缓存文件,并且更新过期时间 */
      NSFileManager *fileManager = [[NSFileManager alloc] init];
      
      if ([fileManager fileExistsAtPath:localURL] == YES){
        cachedFileExists = YES;
        cachedFileData = [NSData dataWithContentsOfFile:localURL];
        if (cachedFileData != nil){
          cachedFileDataCanBeLoaded = YES;
        } /* if (cachedFileData != nil){ */
      } /* if ([fileManager fileExistsAtPath:localURL] == YES){ */
      
      [fileManager release];
      
      /* 更新缓存时间 */
      
      if (paramUpdateExpiryDateIfInCache == YES){
        
        NSDate *newExpiryDate = 
        [NSDate dateWithTimeIntervalSinceNow:
         paramURLMustExpireInSeconds];
        
        NSLog(@"Updating the expiry date from %@ to %@.", 
              expiryDate, 
              newExpiryDate);
        
        [itemDictionary setObject:newExpiryDate
                           forKey:CachedKeyExpiryDate];
        
        NSNumber *expires = 
        [NSNumber numberWithFloat:paramURLMustExpireInSeconds];
        
        [itemDictionary setObject:expires
                           forKey:CachedKeyExpiresInSeconds];
      }
      
    } /* if (cachedFileHasExpired == NO){ */
    
  }
  
  if (cachedFileIsBeingDownloaded == YES){
    NSLog(@"这个文件已经正在下载...");
    return(YES);
  }
  
  if (fileHasBeenCached == YES){
    
    if (cachedFileHasExpired == NO &&
        cachedFileExists == YES &&
        cachedFileDataCanBeLoaded == YES &&
        [cachedFileData length] > 0 &&
        cachedFileIsFullyDownloaded == YES){
      
      /* 如果文件有缓存而且没有过期 */
      
      NSLog(@"文件有缓存而且没有过期.");
      
      [self.delegate 
       cachedDownloadManagerSucceeded:self
       remoteURL:[NSURL URLWithString:paramURLAsString]
       localURL:[NSURL URLWithString:localURL]
       aboutToBeReleasedData:cachedFileData
       isCachedData:YES];
      
      return(YES);
      
    } else {
      /* 如果文件没有被缓存,获取缓存失败 */
      NSLog(@"文件没有缓存.");
      [self.cacheDictionary removeObjectForKey:paramURLAsString];
      [self saveCacheDictionary];
    } /* if (cachedFileHasExpired == NO && */
    
  } /* if (fileHasBeenCached == YES){ */
  
  /* 去下载文件 */
  
  
  NSNumber *expires = 
  [NSNumber numberWithFloat:paramURLMustExpireInSeconds];
  
  NSMutableDictionary *newDictionary = 
  [[[NSMutableDictionary alloc] init] autorelease];
  
  [newDictionary setObject:expires 
                    forKey:CachedKeyExpiresInSeconds];
  
  
  localURL = [paramURLAsString
              stringByAddingPercentEscapesUsingEncoding:
              NSUTF8StringEncoding];
  
  localURL = [localURL stringByReplacingOccurrencesOfString:@"://"
                                                 withString:@""];
  
  localURL = [localURL stringByReplacingOccurrencesOfString:@"/"
                                                 withString:@"{1}quot;];
  
  localURL = [localURL stringByAppendingPathExtension:@"cache"];
  
  NSString *documentsDirectory = 
  [self documentsDirectoryWithTrailingSlash:NO];
  
  localURL = [documentsDirectory 
              stringByAppendingPathComponent:localURL];
  
  [newDictionary setObject:localURL
                    forKey:CachedKeyLocalURL];
  
  [newDictionary setObject:now
                    forKey:CachedKeyDownloadStartDate];
  
  [self.cacheDictionary setObject:newDictionary
                           forKey:paramURLAsString];
  
  [self saveCacheDictionary];
  
  CacheItem *item = [[[CacheItem alloc] init] autorelease];
  [item setDelegate:self];
  [item startDownloadingURL:paramURLAsString];
  
  return(result);
  
}

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

@protocol CacheItemDelegate <NSObject>
//下载成功执行该方法
- (void) cacheItemDelegateSucceeded
  :(CacheItem *)paramSender
  withRemoteURL:(NSURL *)paramRemoteURL
  withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData;

//下载失败执行该方法
- (void) cacheItemDelegateFailed
  :(CacheItem *)paramSender
  remoteURL:(NSURL *)paramRemoteURL
  withError:(NSError *)paramError;

@end

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

//缓存项的委托方法
- (void) cacheItemDelegateSucceeded:(CacheItem *)paramSender
         withRemoteURL:(NSURL *)paramRemoteURL
        withAboutToBeReleasedData:(NSData *)paramAboutToBeReleasedData{
  
  //从缓存字典中获取该缓存项的相关数据
  NSMutableDictionary *dictionary = 
  [self.cacheDictionary objectForKey:[paramRemoteURL absoluteString]];
  //取当前时间
  NSDate *now = [NSDate date];
  //获取有效时间
  NSNumber *expiresInSeconds = [dictionary 
                                objectForKey:CachedKeyExpiresInSeconds];
  //转换成NSTimeInterval
  NSTimeInterval expirySeconds = [expiresInSeconds floatValue];
  //修改字典中缓存项的下载结束时间
  [dictionary setObject:[NSDate date]
                 forKey:CachedKeyDownloadEndDate];
  //修改字典中缓存项的缓存过期时间
  [dictionary setObject:[now dateByAddingTimeInterval:expirySeconds]
                 forKey:CachedKeyExpiryDate];
  //保存缓存字典
  [self saveCacheDictionary];
  
  NSString *localURL = [dictionary objectForKey:CachedKeyLocalURL];
  
  /* 将下载的数据保持到磁盘 */
  if ([paramAboutToBeReleasedData writeToFile:localURL
                                   atomically:YES] == YES){
    NSLog(@"缓存文件到磁盘成功.");
  } else{
    NSLog(@"缓存文件到磁盘失败.");
  }
  //执行缓存管理的委托方法
  [self.delegate 
   cachedDownloadManagerSucceeded:self
   remoteURL:paramRemoteURL
   localURL:[NSURL URLWithString:localURL]
   aboutToBeReleasedData:paramAboutToBeReleasedData
   isCachedData:NO];
  
}

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

//缓存项失败失败的委托方法
- (void) cacheItemDelegateFailed:(CacheItem *)paramSender
                       remoteURL:(NSURL *)paramRemoteURL
                       withError:(NSError *)paramError{
  
  /* 从缓存字典中移除缓存项,并发送一个委托 */
  
  if (self.delegate != nil){
    
    NSMutableDictionary *dictionary = 
    [self.cacheDictionary 
     objectForKey:[paramRemoteURL absoluteString]];
    
    NSString *localURL = [dictionary 
                          objectForKey:CachedKeyLocalURL];
    
    [self.delegate
     cachedDownloadManagerFailed:self
     remoteURL:paramRemoteURL
     localURL:[NSURL URLWithString:localURL]
     withError:paramError];
  }
  
  [self.cacheDictionary 
   removeObjectForKey:[paramRemoteURL absoluteString]];
  
}

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

IOS开发缓存机制之—本地缓存机制的更多相关文章

  1. iOS开发网络篇—搭建本地服务器

    iOS开发网络篇—搭建本地服务器 一.简单说明 说明:提前下载好相关软件,且安装目录最好安装在全英文路径下.如果路径有中文名,那么可能会出现一些莫名其妙的问题. 提示:提前准备好的软件 apache- ...

  2. iOS开发如何实现消息推送机制

    一.关于推送通知 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的.一般说来,当要显示消息 ...

  3. ios开发之数据的持久化存储机制

    IOS中数据的持久化保存这块内容,类似于Android中文件的几种常见的存储方式. 对于数据的持久化存储,ios中一般提供了4种不同的机制. 1.属性列表 2.对象归档 3.数据库存储(SQLite3 ...

  4. iOS开发 -李洪强-清除缓存

    // //  SetViewController.m //  dfhx // //  Created by dfhx_iMac_001 on 16/4/5. //  Copyright © 2016年 ...

  5. iOS开发之功能模块--本地序列化

    下面只展示项目开发中,本地序列化的示例代码: AuthenticationManager.h #import <Foundation/Foundation.h> #import " ...

  6. Ios开发中UILocalNotification实现本地通知实现提醒功能

    这两天在做一个日程提醒功能,用到了本地通知的功能,记录相关知识如下: 1.本地通知的定义和使用: 本地通知是UILocalNotification的实例,主要有三类属性: scheduled time ...

  7. iOS开发查看手机app本地存储的文件

    开发过程中,有时会在本地存储一些文件,但是我们不确定有没有存上,可以通过以下方法来查看测试手机上本地存储的文件: 1.选择xcode上面的window下面的Devices 2.先在左边选中你当前的设备 ...

  8. iOS开发网络篇—搭建本地服务器(待整理)

      一.简单说明 说明:提前下载好相关软件,且安装目录最好安装在全英文路径下.如果路径有中文名,那么可能会出现一些莫名其妙的问题. 提示:提前准备好的软件 apache-tomcat-6.0.41.t ...

  9. [iOS 开发] app无法访问本地相册,且不显示在设置 -隐私 - 照片中

    近几天在使用iOS8的Photos Framework访问本地相册时,app即不会弹出是否允许访问提示框,也无法显示在iPhone的设置-隐私-照片的访问列表中,代码如下: PHAuthorizati ...

随机推荐

  1. myeclipse导出javadoc时特殊字符 尖括号

    源字符<?xml version="1.0" encoding="UTF-8" standalone="yes"?> javad ...

  2. didMoveToSuperView 引发的思考

    1. - (void)didMoveToSuperview 通知视图已经移动到一个新的父视图中 2. /**系统自动调用(留给子类去实现)**/ - (void)didAddSubview:(UIVi ...

  3. Java中关于HashMap的使用和遍历(转)

    Java中关于HashMap的使用和遍历 分类: 算法与数据结构2011-10-19 10:53 5345人阅读 评论(0) 收藏 举报 hashmapjavastringobjectiterator ...

  4. C# devExpress BandedGridView属性 备忘

    BandedGridView属性备忘 StringBuilder sb = new StringBuilder(); DevExpress.XtraGrid.Views.BandedGrid.Band ...

  5. Cannot find PHPUnit in include path phpstorm

    This is the way to do it without using composer, and using your global phpunit.Phpunit now comes wit ...

  6. AD进行行PCB DRC检查时,软件提示...report_drc.xsl不存在

    之前装过一次AD软件没有报过这样的错误,卸掉后重新装了之后,在对电气规则检查检查时“软件提示...report_drc.xsl不存在”. 原因:之前装的目录默认在C盘下,所以AD软件输出的报告也是默认 ...

  7. 基于M9K块配置ROM的LCD12864图片显示实验

    先上传三张图片在说                             由于串口传输速度较慢,故此实验是在“LCD12864 液晶显示-汉字及自定义显示(并口)”基础上进一步修改而来.在写代码之前 ...

  8. GB2312 编码

    GB2312编码范围:A1A1-FEFE,其中汉字编码范围:B0A1-F7FE. GB2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用.GB2312编 ...

  9. boost库之shared_ptr

    shared_ptr 编辑 目录 1简介 2作用 3历史 4概要 5用法 ▪ 删除共享对象 ▪ 标准容器 1简介编辑 shared_ptr是一种智能指针(smart pointer). 2作用编辑 s ...

  10. 屌爆了的两个在线编辑网站runjs和jsbin

    作者:zccst HTML5+css3做的小游戏 http://timelineapp.pointstone.org/coreball/game.html 切图工具:http://kuaiqie.co ...