Objective-c下具有下载功能的NSURLCache类CustomURLCache

最近在做iOS APP开发的过程中遇到了这么一个问题:我开发的是一个阅读类的App,正文界面是通过UIWebViewController来实现的,现在要实现文章离线阅读功能。即将当前web页面所有的资源请求结果都下载都本地。在网上找了很多方法,发现都不是很好。后来还是决定从cache入手:iOS自带的NSURLCache并不支持将cache下载到自定义的目录底下,所以只有复写NSURLCache这个类来实现这些功能。在网上找到一个他人写的CustomURLCache的类,但是发现使用的过程中程序会崩,于是一步一步调试代码,定位到该类的错误,对代码进行了修改,修复了这个bug。而随着iOS8的推出,发现设置自定义的URLCache必须要在AppDelegate.m的 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 函数中使用才行,否则无效。无奈,只好进一步对代码进行修改。终于可以适配ios8了。今天将这部分代码分享出来,希望对大家有用。代码我已经托管到github上,大家可以自行下载。

同时为了兼容系统NSURLCache的功能,CustomURLCache有两种模式,NORMAL_MODE和DOWNLOAD_MODE,NORMALMODE下的CustomCache和NSURLCache的功能是一样的;而DOWNLOADMODE下的CustomURLCache则可以实现包含自定义下载目录,设置过期时间的子功能的下载功能。

接下来用例子教大家如何使用CustomURLCache这个类。假设我们在做一个阅读类的App,正文页面用UIWebViewController来展示。现在需要实现一个离线下载的功能,即将当前网页的所有访问到的资源(html文件,图片,JavaScript,CSS等等)全部都保存到本地。用CustomURLCache就可以很容易地实现这个功能。

在AppDelegate.m中将CustomURLCache设置为全局URLCache

上文已经讲到了,在iOS8中,设置全局URLCache必须在AppDelegate.m中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions实现,所以第一步就是将我们CustomURLCache在该函数中设置为全局URLCache:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    ......
    //Init custom cache
    
    CustomURLCache *_mCache = [[CustomURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
                                                diskCapacity:200 * 1024 * 1024
                                                    diskPath:nil
                                                   cacheTime:0
                                                subDirectory:nil];
    [NSURLCache setSharedURLCache:_mCache];

    return YES;
}

这段代码将我们的CustomURLCache设置为了全局的URLCache,其中的参数有如下意义:

  • diskCapacity:就是用户设置的这个cache的大小
  • diskPath:cache的下载路径,如果设置为nil的话,那么该类将会自动将当前应用的缓存目录作为下载路径
  • cacheTime:cache的过期时间,如果设置为0的话,那么cache将永远不会过期
  • subDirectory:这是位于diskPath下的子目录,意为当前的cache会存在哪个子目录中。不过在初始化的时候只用将其设置为nil即可

值得说明的时候,在初始化之后其实没有任何变化,因为这个时候的默认模式是NORMAL_MODE,即跟NSURLCache是一样的。所以感觉不出什么不一样。不要着急,很快我们就需要使用到DOWNLOAD_MODE了。

将CustomURLCache设置为DOWNLOAD_MODE

好了,假设我们在第一个页面选中了一篇文章,然后点击计入了正文页面,正文页面的布局很简单,就是一个UIWebView,我们需要用其显示一个网页,并且将所有需要请求的资源都下载到本地自定义的目录中。这个时候就需要用到DOWNLOAD_MODE了。实现很简单,在正文界面的UIViewController的- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil或者init函数中将CustomURLCache设置为DOWNLOAD_MODE:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        
        ......

    
        _mCache = (CustomURLCache *)[NSURLCache sharedURLCache];
        [_mCache changeToDownloadMode:_subDir];
        [NSURLCache setSharedURLCache:_mCache];
    }
    return self;
}

现在我们就成功地将CustomURLCache设置为了下载模式,在执行[_mCache changeToDownloadMode:_subDir];时需要指定下载子目录,就位于diskPath下的下载子目录。接下来所有的web请求资源都会下载到我们的自定义目录了。

将CustomURLCache恢复到NORMAL_MODE

好了,现在web页面也展现出来了,所有的web请求资源都下载到本地了,该退出了。回退到第一个页面,并且不再需要DOWNLOAD_MODE,我们要将其恢复为NORMAL_MODE,那么在当前UIViewController下的dealloc函数或者其他用户自定义的退出函数中加入如下代码(在这里我使用的是自己的退出函数):

- (void)backToMainpage:(id)sender {
    if (不再需要文章缓存) {
        NSLog(@"删除文章缓存");
        [_mCache removeCustomRequestDictionary ];  
    }
    else
    {
    //do nothing
     }
    //不再使用自定义缓存,而是换到系统的缓存
    [_mCache changeToNormalMode];
    [self.navigationController popViewControllerAnimated:YES];
}

好了,如果你没有删除文章缓存,那么当我们再次就如正文页面的时候,你会发现页面很快就加在完成了。原因很简单,当你再次进入该UIViewController的时候,程序又会执行[_mCache changeToDownloadMode:_subDir];这时,CustomURLCache在接到URL请求的时候会先到_subDir路径下去找是否有缓存的资源,如果找到了,并且还不过期,那么就会自动加在本地的资源,如果没有找到或者过期了,那么会重新通过网络请求。所以你知道为什么这么快就完成了,因为所有的资源都是在本地的!

好了,简单地使用就介绍到这里了。希望对大家有帮助。同时,如果大家喜欢的话就请在GitHub上fork或者star一下吧,万分感谢!地址是:点击打开链接

上一篇:A+B for Input-Output Practice (III)


下一篇:政务系统故障容灾备份解燃眉之急