清除缓存

现在的大多数APP都带有缓存功能,尤其是使用一些第三方框架,比如SDWebImage 就自带缓存功能。当用了一段时间后,缓存就会越来愈大,如果不清除的话就会影响用户体验和性能,所以大多📚APP加了清除缓存的功能。这里的主要实现思路也是根据SDWebImage 的思路去实现。这里需要用到一个 simpholders工具可以直接查看沙盒下的东西。

1.获取缓存

既然是参考SDWebImage去实现,那么首先就是进去看看他是怎么实现的,首先先导入 #import <SDWebImage/SDImageCache.h>头文件,这是SDWebImage 的一个管理缓存的类,点进去在文件中搜索关键词cache,会看到好多结果,一直往下拉,会看到这么几个方法:

我们看到里面有这样一个方法,很明显这个是我们常写的单例。待会儿会用到
/**
 * Returns global shared cache instance
 *
 * @return SDImageCache global instance
 */
+ (SDImageCache *)sharedImageCache;

再往下就看到下面的方法:

Paste_Image.png

这些根据字面意思很容易看出来都是干什么的。既然这里我们的第一步是想先获取缓存,那么我们就主要先看一下getSize方法,点击进去会看到如下方法:

- (NSUInteger)getSize {
    __block NSUInteger size = 0;
    dispatch_sync(self.ioQueue, ^{
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];
        for (NSString *fileName in fileEnumerator) {
            NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
            NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
            size += [attrs fileSize];
        }
    });
    return size;
}

这里我们看到需要用到一个 _fileManager的对象,这是一个NSFileManager 的对象,我们先回到我们的类文件中,去调用一下getSize 方法获取缓存。通过上面的工具我们可以查看SDWebImage的缓存文件:在default文件下面就是

Paste_Image.png
 NSUInteger  cach = [[SDImageCache sharedImageCache] getSize];
 NXLog(@"sdweb-----caches:%lu",(unsigned long)cach);

打印结果如下
2017-02-06 23:41:09.554 BaiSi[44360:1314372] sdweb-----caches:2824429

通过右键查看文件,得到结果如下,比打印结果大是因为这里还包括了隐藏文件.DS

Paste_Image.png

我们也以获取这个文件夹缓存的大小为试验,看看自己写的结果是否和SD 的一样:

- (void)clearCach{
    //1.先获取cache 目录的路径
    NSString * cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 这里先以获取 default  文件夹大小做实验,看看是否和 SDImageCache 获取的一样
    NSString * directorPath = [cachePath stringByAppendingPathComponent:@"default"];
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    NSDictionary * attrs = [fileManager attributesOfItemAtPath:directorPath error:nil];
    //4.最后获取size
    NSUInteger size = [attrs fileSize];
    NXLog(@"custom----size:%lu",(unsigned long)size);
    
    //打印结果如下
    // 2017-02-06 23:47:01.843 BaiSi[44442:1317486] custom----size:136
}

可以看到最后的结果和SD差了相当多,初步判断这个可能不可以获取文件夹的大小,智能或许文件的大小,为了验证,我们拼接一个文件的路径,在上面第一步获取路径下面再加上一个image 的路径,如下:

NSString * imagePath = [directorPath stringByAppendingPathComponent:@"com.hackemist.SDWebImageCache.default/aa96232fe024aeba562815618fd5e02a.jpg"];
第三步里的directorPath  改成  imagePath ,运行

//打印结果如下:
2017-02-07 00:00:04.244 BaiSi[44688:1326243] custom----size:169817

我们直接通过查看图片属性结果如下:

Paste_Image.png

和我们得到的结果一模一样,说明该属性方法只是获取文件的大小,所以我们需要遍历目的文件夹下的所有文件,将大小累加起来,就是该文件夹的总大小。
在SD 的 getSize 方法里面我们可以看到他调用的是 NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtPath:self.diskCachePath];方法,然后去遍历fileEnumerator。这里我们可以用另一个属性subpathsAtPath:directorPath,改写clearCach如下:

- (void)clearCach{
    //1.先获取cache 目录的路径
    NSString * cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 这里先以获取 default  文件夹大小做实验,看看是否和 SDImageCache 获取的一样
    NSString * directorPath = [cachePath stringByAppendingPathComponent:@"default"];
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    //这个可以获取所有的子文件及子文件下的文件
    NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
    NXLog(@"subPaths:%@",subPaths);
    
    
    NSDictionary * attrs = [fileManager attributesOfItemAtPath:directorPath error:nil];
    //4.最后获取size
    NSUInteger size = [attrs fileSize];
    NXLog(@"custom----size:%lu",(unsigned long)size);
    
    //打印结果如下
    // 2017-02-06 23:47:01.843 BaiSi[44442:1317486] custom----size:136
}

打印结果:

Paste_Image.png

可以看到 这个方法获取到了default 文件夹下的子文件夹及子文件夹下的文件以及隐藏文件.接下来我们继续修改clearCach 方法:

- (void)clearCach{
    //1.先获取cache 目录的路径
    NSString * cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 这里先以获取 default  文件夹大小做实验,看看是否和 SDImageCache 获取的一样
    NSString * directorPath = [cachePath stringByAppendingPathComponent:@"default"];
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    //这个可以获取所有的子文件及子文件下的文件
    NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
    NSUInteger totoalSize = 0 ;
    for (NSString * path in subPaths) {
        NSString * filePath = [directorPath stringByAppendingPathComponent:path];
        
        NSDictionary * attrs = [fileManager attributesOfItemAtPath:filePath error:nil];
        //4.最后获取size
        totoalSize += [attrs fileSize];
    }
 
    NXLog(@"custom----size:%lu",(unsigned long)totoalSize);
    
}

最终打印结果如下

Paste_Image.png

基本上和SD 自己获取的大小差不多,这里比SD 的大是因为这里没有排除隐藏文件.DS_Store和下面那个default 文件夹,接下来对这两个文件进行排除:

- (void)clearCach{
    //1.先获取cache 目录的路径
    NSString * cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 这里先以获取 default  文件夹大小做实验,看看是否和 SDImageCache 获取的一样
    NSString * directorPath = [cachePath stringByAppendingPathComponent:@"default"];
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    //这个可以获取所有的子文件及子文件下的文件
    NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
    NSUInteger totoalSize = 0 ;
    for (NSString * path in subPaths) {
        NSString * filePath = [directorPath stringByAppendingPathComponent:path];
        
        //判断是否是隐藏文件
        if ([filePath containsString:@".DS"]) {
            continue;
        }
        //判断是否是文件夹
        BOOL isDirector;
        BOOL fileExist = [fileManager fileExistsAtPath:filePath isDirectory:&isDirector];
        if (!fileExist || isDirector) {
            continue;
        }
        NSDictionary * attrs = [fileManager attributesOfItemAtPath:filePath error:nil];
        //4.最后获取size
        totoalSize += [attrs fileSize];
    }
 
    NXLog(@"custom----size:%lu",(unsigned long)totoalSize);   
}

这个时候得到的结果就和SD 的一样了。
上面的结果基本上已经可以满足功能了,不过为了复用性,还得进行一些修改,最好是能传入一个文件夹的路径然后获取大小,这里吧方法名改了一下:

- (NSUInteger)getFileSize:(NSString *)directorPath{
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    //这个可以获取所有的子文件及子文件下的文件
    NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
    NSUInteger totoalSize = 0 ;
    for (NSString * path in subPaths) {
        NSString * filePath = [directorPath stringByAppendingPathComponent:path];
        
        //判断是否是隐藏文件
        if ([filePath containsString:@".DS"]) {
            continue;
        }
        //判断是否是文件夹
        BOOL isDirector;
        BOOL fileExist = [fileManager fileExistsAtPath:filePath isDirectory:&isDirector];
        if (!fileExist || isDirector) {
            continue;
        }
        NSDictionary * attrs = [fileManager attributesOfItemAtPath:filePath error:nil];
        //4.最后获取size
        totoalSize += [attrs fileSize];
    }
 
    NXLog(@"custom----size:%lu",(unsigned long)totoalSize);
    return  totoalSize;
}

// 然后在调用的地方如下就可以了:

 //1.先获取cache 目录的路径
    NSString * cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // 这里先以获取 default  文件夹大小做实验,看看是否和 SDImageCache 获取的一样
    NSString * directorPath = [cachePath stringByAppendingPathComponent:@"default"];
    
 [self getFileSize:directorPath];

上面的结果已经基本满足功能了。但是如果是遇到一个很大的文件夹的话,该操作可能会造成卡线程,所以最好能放到异步线程里去操作。由于是异步操作,这里就不适合用return 来返回结果了,可以用block的形式来传结果:

- (void)getFileSize:(NSString *)directorPath completion:(void(^)(NSUInteger totoalSize))completion{
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //这个可以获取所有的子文件及子文件下的文件
        NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
        NSUInteger totoalSize = 0 ;
        for (NSString * path in subPaths) {
            NSString * filePath = [directorPath stringByAppendingPathComponent:path];
            
            //判断是否是隐藏文件
            if ([filePath containsString:@".DS"]) {
                continue;
            }
            //判断是否是文件夹
            BOOL isDirector;
            BOOL fileExist = [fileManager fileExistsAtPath:filePath isDirectory:&isDirector];
            if (!fileExist || isDirector) {
                continue;
            }
            NSDictionary * attrs = [fileManager attributesOfItemAtPath:filePath error:nil];
            //4.最后获取size
            totoalSize += [attrs fileSize];
        }
        //完成之后回调
        //这里需要在主线程去回调。不然接收不到值。这里可以用同步操作,不会太耗时
        dispatch_sync(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(totoalSize);
            }
        });
       
    });
}

到这一步已经可以完成了。不过我们还可以在完善一点,因为我们永远无法想象用户会做什么操作。同样我们将这样的方法丢给同事去调用,也无法去想象他们会传一些什么稀奇古怪的参数,比如,他直接给你传个文件的路径而不是文件夹,所以我们还得加一些异常处理,比如抛异常,就像苹果经常干的那样。
最终我们修改后的方法如下:

- (void)getFileSize:(NSString *)directorPath completion:(void(^)(NSUInteger totoalSize))completion{
   
    //2.声明一个文件管理对象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    
    // 在这里做一些异常的处理
    //判断是否是文件夹
    BOOL isDirector;
    BOOL fileExist = [fileManager fileExistsAtPath:directorPath isDirectory:&isDirector];
    if (!fileExist || !isDirector) {
       NSException * exception = [NSException exceptionWithName:@"PathError" reason:@"hey,man,这里需要的是一个文件夹的路径,并且是存在的" userInfo:nil];
        [exception raise];
    }
    
    
    //3.在 SDImageCache 的getSize 方法里面我们看到调用了一个方法  NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];所以我们也模仿他去做
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //这个可以获取所有的子文件及子文件下的文件
        NSArray * subPaths = [fileManager subpathsAtPath:directorPath];
        NSUInteger totoalSize = 0 ;
        for (NSString * path in subPaths) {
            NSString * filePath = [directorPath stringByAppendingPathComponent:path];
            
            //判断是否是隐藏文件
            if ([filePath containsString:@".DS"]) {
                continue;
            }
            //判断是否是文件夹
            BOOL isDirector;
            BOOL fileExist = [fileManager fileExistsAtPath:filePath isDirectory:&isDirector];
            if (!fileExist || isDirector) {
                continue;
            }
            NSDictionary * attrs = [fileManager attributesOfItemAtPath:filePath error:nil];
            //4.最后获取size
            totoalSize += [attrs fileSize];
        }
        //完成之后回调
        //这里需要在主线程去回调。不然接收不到值。这里可以用同步操作,不会太耗时
        dispatch_sync(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(totoalSize);
            }
        });
       
    });
}

然后在调用的时候传入一个错误的地址,正如我们所料,Xcode崩了,在控制台出来结果如下:


Paste_Image.png

OK,可以收工了。

清除缓存

清楚缓存相对比较简单,直接贴代码吧:

- (void)removeFiles:(NSString *)directorPath{
     NSFileManager * manager = [NSFileManager defaultManager];
    //这个方法是获取该文件下的所有子文件夹,不会获取子文件夹里的文件(这里没必要了,直接删除文件夹就可以)
    NSArray * subDirectors = [manager contentsOfDirectoryAtPath:directorPath error:nil];
    for (NSString * path in subDirectors) {
        NSString * filePath = [directorPath stringByAppendingPathComponent:path];
        //删除文件夹
        [manager removeItemAtPath:filePath error:nil];
    }    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容