为你的APP添加缓存清理功能吧!

前言

iOS 由于沙盒机制,不同APP之间不能相互访问彼此的目录,所以iOS 没有像安卓那样有大量的第三方APP来帮我们删除Cache,这必须是每个APP各扫门前雪。作为一个为用户着想的developer,为自己的APP添加缓存清理功能是非常有必要的~

iOS的沙盒目录

清理缓存我们首先得知道缓存在哪!

iOS8之前的sandbox目录

先来了解每个文件夹的作用如下:

文件夹 存放内容 是否会被iTunes同步
myAPP.app 该目录包含了应用程序本身的数据,包括资源文件和可执行文件等。程序启动以后,会根据需要从该目录中动态加载代码或资源到内存,这里用到了lazy loading的思想。
Documents 我们可以将应用程序的数据文件保存在该目录下。不过这些数据类型仅限于不可再生的数据,可再生的数据文件应该存放在Library/Cache目录下。
Library 苹果建议用来存放默认设置或其它状态信息。 是,但是要除了Caches子目录外
Library/Caches 主要是缓存文件,用户使用过程中缓存都可以保存在这个目录中。比如网络请求的数据。鉴于此,应用程序通常还需要负责删除这些文件。
Library/Preferences 应用程序的偏好设置文件。我们使用NSUserDefaults写的设置数据都会保存到该目录下的一个plist文件中
temp 各种临时文件,保存应用再次启动时不需要的文件。而且,当应用不再需要这些文件时应该主动将其删除,因为该目录下的东西随时有可能被系统清理掉,目前已知的一种可能清理的原因是系统磁盘存储空间不足的时候。

所以我们所谓的清理缓存就是要对 “Cache”文件夹 “动手动脚”!

iOS8之后沙盒路径的变化

Apple在iOS8中对数据(sandbox)和(.app)都进行了分离,并且采用最新的沙盒机制。

  • Legacy location:~/Library/Application Support/
  • Legacy location:~/Library/Containers//Data/Library/Application Support//

其中每一次重启App都会发生改变,增加了安全性。分离出数据部分,目的是不同的的APP可以共享数据。

例如我用模拟器打印出如下的路径:

  • .app:/Users/lz/Library/Developer/CoreSimulator/Devices/FFF8CF48-2B3A-488F-8FA9-A61D867FBD81/data/Containers/Bundle/Application/CDC5A46C-FF3C-436E-B8F9-0F823A12AE12/Fingertip.app
  • sandbox:/Users/lz/Library/Developer/CoreSimulator/Devices/FFF8CF48-2B3A-488F-8FA9-A61D867FBD81/data/Containers/Data/Application/F60A5FC6-669B-41DA-A33D-90AF2FFF6C45

缓存清理

缓存清理说白了就要对 sandbox 底下的 caches 文件夹下的文件进行删除,当然你可以选择性的删除,保留一些没有比用进行的清除的文件,比如,用户的头像图片等等。

我们必须实例化一个 NSFileManager 来做文件操作:

1
2
@property (strong, nonatomic)  NSFileManager *fileManager;
self.fileManager = [NSFileManager defaultManager];

获取沙盒目录下的Caches目录

有以下两种方法:

NSHomeDirectory()

1
NSString *cacheFilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];

NSSearchPathForDirectoriesInDomains

1
2
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheFilePath = [paths objectAtIndex:0];

计算缓存大小

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
- (void)getCacheSize:(void (^)(NSString *size))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

float folderSize = 0;
if ([_fileManager fileExistsAtPath:_cacheFilePath]) {
NSArray *subPaths = [_fileManager subpathsAtPath:_cacheFilePath];

for (NSString *fileName in subPaths) {
NSString *absolutePath = [_cacheFilePath stringByAppendingPathComponent:fileName];
folderSize += [[_fileManager attributesOfItemAtPath:absolutePath error:nil]fileSize];
}
folderSize = folderSize / (1024*1024);
}

NSString *sumSize;
if (folderSize < 0.1)
{
sumSize = @"0M";
}else
{
sumSize = [NSString stringWithFormat:@"%.2fM",folderSize];
}

dispatch_async(dispatch_get_main_queue(), ^{
completion(sumSize);
});

});
}

说明:由于我们删除 caches 我文件夹底下的文件之后,可能系统会自动生成一些文件,导致缓存计算不为0;我们可以将小于 0.1M 的缓存视为 0M;防止“有强迫症”用户重复点击。

删除Caches文件夹下文件

这里为了有更好的用户体验使用了 MBProgressHUD,相信大家也不陌生。

具体代码如下:

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
- (void)cleanCache:(void(^)(void))completion
{
UIViewController *topController = [[UIApplication sharedApplication].delegate.window rootViewController];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:topController.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = NSLocalizedString(@"MORE_CLEANING", nil);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

if ([_fileManager fileExistsAtPath:_cacheFilePath]) {
NSArray *subPaths = [_fileManager subpathsAtPath:_cacheFilePath];

for (NSString *fileName in subPaths) {

NSString *absolutePath = [_cacheFilePath stringByAppendingPathComponent:fileName];
BOOL isDirectory = NO;
[_fileManager fileExistsAtPath:absolutePath isDirectory:&isDirectory];

if (!isDirectory) {
NSError *error;
[_fileManager removeItemAtPath:absolutePath error:&error];
NSLog(@"Error removing file at path: %@", error.localizedDescription);
}
}

dispatch_async(dispatch_get_main_queue(), ^{
hud.mode = MBProgressHUDModeText;
hud.labelText = NSLocalizedString(@"MORE_DONE", nil);
[hud hide:YES afterDelay:1.0];
completion();
});
}
});
}

说明:如果是使用模拟器,我们我可以直接使用一下代码:

1
2
3
4
5
if ([_fileManager fileExistsAtPath:_cacheFilePath]) {
NSError *error;
[_fileManager removeItemAtPath:_cacheFilePath error:&error];
NSLog(@"Error removing file at path: %@", error);
}

不需要去遍历文件夹判断是文件路径还是文件夹路径,再去删除。真机上是必须这样做的,因为真机运行代码时,是没有权限对文件夹做整一个的删除操作的。

代码地址

具体可以看我写的demo。

iOSCleanCaches

最后的话

最近感觉要学习的东西,太多太多了,博客会慢慢更,书会继续读,代码会努力打。