清除缓存

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

1.获取缓存

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

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

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

清除缓存_第1张图片
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文件下面就是

清除缓存_第2张图片
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

清除缓存_第3张图片
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

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

清除缓存_第4张图片
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
}

打印结果:

清除缓存_第5张图片
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];
    }    
}

你可能感兴趣的:(清除缓存)