iOS开发iOS Developer

iOS 文件管理器与性能提升

2017-05-23  本文已影响201人  zaijianbali

写一个文件管理器并不难,难在如何提升性能?

文件管理器无非就是增删改查

本文的重点在于删和查的新能提升。
普通的目录,我们删除或者查询一个或者多个很快,但如果这个目录文件量达到
举个例子:
1.文件如何归类存储
2.查询目录下超过三个月未访问的文件大小
3.删除超过三个月未访问的文件

针对归类存储,我们的策略很简单,就是语音、图片放到不同的目录下:

/**
*  @brief  视频文件目录的路径
*
*  @return 视频目录路径
*/
+ (NSString *)videoPath;

/**
*  @brief  图片文件目录的路径
*
*  @return 图片目录路径
*/
+ (NSString *)imagePath;

/**
*  @brief  音频目录路径
*
*  @return 音频目录路径
*/
+ (NSString *)audioPath;

针对第二个问题:查询目录下超过三个月未访问的文件大小
我们提供了如下接口:


/**
*  @brief  删除图片文件缓存
*
*  @return 删除成功失败
*/
+ (BOOL)deleteCachedImages;
+ (BOOL)deleteCachedImagesFor3MonthAgo;

/**
*  @brief  删除音频文件缓存
*
*  @return 删除成功失败
*/
+ (BOOL)deleteCachedAudioes;
+ (BOOL)deleteCachedAudioesFor3MonthAgo;

/**
*  @brief  删除视频文件缓存
*
*  @return 删除成功失败
*/
+ (BOOL)deleteCachedVideoes;
+ (BOOL)deleteCachedVideoesFor3MonthAgo;

/**
*  @brief  删除3个月前的图片、语音、视频数据
*/
+ (BOOL)deleteCachedForderFor3MonthAgo;
/**
*  @brief  删除图片、语音、视频数据
*/
+ (BOOL)deleteCachedForder;

关于第三个问题:删除超过三个月未访问的文件
我们提供接口如下:

/**
 *  @brief  图片文件夹大小,单位字节B(Byte)
 */
+ (int64_t)imageFolderSize;

/**
 *  @brief  音频文件夹大小,单位字节B(Byte)
 */
+ (int64_t)audioFolderSize;

/**
 *  @brief  视频文件夹大小,单位字节B(Byte)
 */
+ (int64_t)videoFolderSize;

/**
 *  三个月前的图片、语音、视频文件大小,单位字节B(Byte)
 */
+ (int64_t)folderSizeFor3MonthAgo;
/**
 * 图片、语音、视频的文件大小,单位字节B(Byte)
 */
+ (int64_t)cachedFolderSize;

/**
 *  @brief  系统总容量,单位:B
 */
+ (float)totalDiskSpace;

/**
 *  @brief  系统剩余可用空间大小,单位:B
 */
+ (float)freeDiskSpace;

/**
 *  @brief  当前应用使用的硬盘空间,耗时操作,单位:B
 */
+ (float)appUsedSpace;

分割线


下面说说性能,一般情况下为了方便:我们都是用OC代码写,但OC是基于C的封装,我们看看OC如何访问文件的。

    // 方法1:使用NSFileManager来实现获取文件大小
+ (long long)fileSizeAtPath1:(NSString *)filePath{
    NSFileManager* manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:filePath]){
        return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];
    }
    return 0;
}

    // 方法1:使用unix c函数来实现获取文件大小
+ (long long)fileSizeAtPath2:(NSString *)filePath{
    struct stat st;
    if(lstat([filePath cStringUsingEncoding:NSUTF8StringEncoding], &st) == 0){
        return st.st_size;
    }
    return 0;
}
#pragma mark 获取目录大小


    //实测 :1000个文件 读取 平均时间为:0.079914
    // 方法1:循环调用fileSizeAtPath1
+ (long long)folderSizeAtPath1:(NSString *)folderPath{
    CFAbsoluteTime startTime =CFAbsoluteTimeGetCurrent();
    NSFileManager* manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:folderPath]) return 0;
    NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];
    NSString* fileName;
    long long folderSize = 0;
    while ((fileName = [childFilesEnumerator nextObject]) != nil){
        NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];
        folderSize += [self fileSizeAtPath1:fileAbsolutePath];
    }
    CFAbsoluteTime endTime =CFAbsoluteTimeGetCurrent();
    NSLog(@"ttttttttttt:%s,%f,%lld",__func__,endTime-startTime,folderSize);
    return folderSize;
}

    // 1000个文件平均时间为:0.02653
    // 方法2:循环调用fileSizeAtPath2
+ (long long)folderSizeAtPath2:(NSString *)folderPath{
    TICK
    NSFileManager* manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:folderPath]) return 0;
    NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];
    NSString* fileName;
    long long folderSize = 0;
    while ((fileName = [childFilesEnumerator nextObject]) != nil){
        NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];
        folderSize += [self fileSizeAtPath2:fileAbsolutePath];
    }
    TOCK
    return folderSize;
}

再来看看如何用c写,其性能如何

    // 方法3:完全使用unix c函数 性能最好
    //实测  1000个文件速度 0.005896
+ (int64_t)folderSizeAtPath:(const char *)folderPath maxTime:(NSNumber *)maxTime{

    CFAbsoluteTime startTime =CFAbsoluteTimeGetCurrent();

    int64_t folderSize = 0;
    DIR* dir = opendir(folderPath);


    if (dir == NULL) return 0;
    struct dirent* child;
    while ((child = readdir(dir))!=NULL) {
        if (child->d_type == DT_DIR && (
                                        (child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
                                        (child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
                                        )) continue;

        int64_t folderPathLength = strlen(folderPath);

        // 子文件的路径地址
        char childPath[1024];
        stpcpy(childPath, folderPath);

        if (folderPath[folderPathLength-1] != '/') {
            childPath[folderPathLength] = '/';
            folderPathLength++;
        }

        stpcpy(childPath+folderPathLength, child->d_name);
        childPath[folderPathLength + child->d_namlen] = 0;

        if (child->d_type == DT_DIR) { // directory
            // 递归调用子目录
            folderSize += [[self class] folderSizeAtPath:childPath maxTime:maxTime];

            // 把目录本身所占的空间也加上
            struct stat st;
            if(lstat(childPath, &st) == 0) folderSize += st.st_size;
        }else if (child->d_type == DT_REG || child->d_type == DT_LNK) { // file or link
            //特殊处理几个月前的内容
            if (maxTime) {
                BOOL shouldCountSize = YES;
                char *p = child->d_name;
                NSString *fileName = [NSString stringWithCString:p encoding:NSUTF8StringEncoding];
                NSComparisonResult result = [fileName compare:[maxTime stringValue]];
                if (result == NSOrderedDescending)//小于三个月不统计
                {
                    shouldCountSize = NO;
                }

                if (!shouldCountSize) {
                    continue;
                }
            }

            struct stat st;
            if(lstat(childPath, &st) == 0) folderSize += st.st_size;
        }
    }
    CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
     NSLog(@"ttttttttttt:%s,%f,%lld",__func__,endTime-startTime,folderSize);
    return folderSize;
}

我们对比一下:
同时访问同样的目录下的1000个文件的用时

方法 用时
方法1 使用NSFileManager 0.079914
方法2 使用c函数 0.02653
方法3 使用C底层函数 0.005896

我们看对比:

使用方法3的速度是方法1的 15倍,等于提升了一个数量级。

最后我们还提供了:大小转换为合适的输出

/**
 *  @brief  获取指定存储小数位数的浮点数,单位G,M,K  
 *   显示规则如下
 *      1.大于1G,转换为G单位的字符串+小数位数
 *      2.大于1M,则转化成M单位的字符串+ 小数位数
 *      3.不到1M,但是超过了1KB,则转化成KB单位+ 小数位数
 *      4.剩下的都是小于1K的,则转化成B单位
 *
 *  @param size          原值大小
 *  @param decimalplaces 指定小数位数大小
 *
 *  @return 获取指定存储小数位数的浮点数,单位G,M,K
 */
+ (NSString *)getStringWithStorage:(float)size decimalplaces:(NSInteger)decimalplaces;

源码见github地址:WCFileController

本文解释权归:子文

如需转载请注明出处,谢谢

来杯可乐催更吧

请子文喝可乐
上一篇下一篇

猜你喜欢

热点阅读