美文网首页iOS常用
iOS 日期转换解析(四)-NSDateFormatter优化

iOS 日期转换解析(四)-NSDateFormatter优化

作者: FieryDragon | 来源:发表于2020-07-28 15:18 被阅读0次

DateFormatter轻度优化探索
DateFormatter深度优化探索
NSDateFormatter最佳实践

获取日期方法

OC:NSDate、NSDateFormatter、NSTimeZone

使用:详见iOS 日期转换解析(一)

C - time()、localtime()或localtime_r()、strftime()

使用:详见iOS 日期转换解析(三)

NSDateFormatter

日期的转换是一件十分消耗性能的事情,所以该模块的优化也是项目优化不可或缺的一部分。而日期的优化也主要集中在NSDateFormatter类的使用上(NSDateFormatter在iOS7及以上系统中是线程安全的,IOS7以前系统现在适配较少,在此不做记录)。

NSDateFormatter官方文档介绍:

Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently, it is typically more efficient to cache a single instance than to create and dispose of multiple instances. One approach is to use a static variable.

创建日期格式化程序不是一个廉价的操作。如果您可能经常使用格式化程序,那么缓存单个实例通常比创建和处理多个实例更有效。一种方法是使用静态变量。

缓存NSDateFormatter

- (void)dateFormatter {
    [self dateFormatterWithCache:1];
    [self dateFormatterWithNOCache:1];
    
    [self dateFormatterWithCache:10];
    [self dateFormatterWithNOCache:10];
    
    [self dateFormatterWithCache:100];
    [self dateFormatterWithNOCache:100];
    
    [self dateFormatterWithCache:1000];
    [self dateFormatterWithNOCache:1000];
    
    [self dateFormatterWithCache:10000];
    [self dateFormatterWithNOCache:10000];
    
    [self dateFormatterWithCache:100000];
    [self dateFormatterWithNOCache:100000];
}
- (void)dateFormatterWithCache:(NSInteger)number {
    CFAbsoluteTime then = CFAbsoluteTimeGetCurrent();
    _formatter= [[NSDateFormatter alloc] init];
    
    for (NSUInteger i = 0; i < number; i++) {
        [_formatter setDateFormat:@"yyyy/MM/dd"];
        NSDate *datenow = [NSDate date];
        [_formatter stringFromDate:datenow];
    }
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    NSLog(@"NSDateFormatter生成一次,获取日期%ld次: %f s", number, now - then);
}

- (void)dateFormatterWithNOCache:(NSInteger)number {
    CFAbsoluteTime then = CFAbsoluteTimeGetCurrent();
    for (NSUInteger i = 0; i < number; i++) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy/MM/dd"];
        NSDate *datenow = [NSDate date];
        [formatter stringFromDate:datenow];
    }
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    NSLog(@"NSDateFormatter生成%ld次,获取日期%ld次: %f s\n",number, number, now - then);
}

NSDateFormatter生成一次,获取日期1次: 0.005116 s
NSDateFormatter生成1次,获取日期1次: 0.000729 s

NSDateFormatter生成一次,获取日期10次: 0.001019 s
NSDateFormatter生成10次,获取日期10次: 0.004491 s

NSDateFormatter生成一次,获取日期100次: 0.002204 s
NSDateFormatter生成100次,获取日期100次: 0.022623 s

NSDateFormatter生成一次,获取日期1000次: 0.003150 s
NSDateFormatter生成1000次,获取日期1000次: 0.052282 s

NSDateFormatter生成一次,获取日期10000次: 0.013738 s
NSDateFormatter生成10000次,获取日期10000次: 0.329568 s

NSDateFormatter生成一次,获取日期100000次: 0.136741 s
NSDateFormatter生成100000次,获取日期100000次: 3.287425 s

通过测试结果发现缓存后对获取时间字符串所用的时间消耗确有很大提升。

NSDateFormatter耗时方法

- (void)dateFormatter {
    [self dateFormatterWithCache:1];
    [self dateFormatterWithNOCache:1];
    
    [self dateFormatterWithCache:10];
    [self dateFormatterWithNOCache:10];
    
    [self dateFormatterWithCache:100];
    [self dateFormatterWithNOCache:100];
    
    [self dateFormatterWithCache:1000];
    [self dateFormatterWithNOCache:1000];
    
    [self dateFormatterWithCache:10000];
    [self dateFormatterWithNOCache:10000];
    
    [self dateFormatterWithCache:100000];
    [self dateFormatterWithNOCache:100000];
}
- (void)dateFormatterWithCache:(NSInteger)number {
    CFAbsoluteTime startTime1  = 0;
    CFAbsoluteTime startTime2 = 0;
    CFAbsoluteTime startTime3 = 0;
    CFAbsoluteTime startTime4 = 0;
    CFAbsoluteTime startTime5 = 0;
    
    CFAbsoluteTime duration1 = 0;
    CFAbsoluteTime duration2 = 0;
    CFAbsoluteTime duration3 = 0;
    
    startTime1 = CFAbsoluteTimeGetCurrent();
    _formatter = [[NSDateFormatter alloc] init];
    startTime2 = CFAbsoluteTimeGetCurrent();
    
    duration1 = startTime2-startTime1;
    NSDate *datenow = [NSDate date];
    for (NSUInteger i = 0; i < number; i++) {
        startTime3 = CFAbsoluteTimeGetCurrent();
        if (i%2==0) {
            [_formatter setDateFormat:@"yyyy/MM/dd"];
        }else {
            [_formatter setDateFormat:@"YYYY:MM:dd HH:mm:ss"];
        }
        startTime4 = CFAbsoluteTimeGetCurrent();
        [_formatter stringFromDate:datenow];
        startTime5 = CFAbsoluteTimeGetCurrent();
        
        duration2 += (startTime4 - startTime3) ;
        duration3 += (startTime5 - startTime4) ;
        
    }
    NSLog(@"缓存NSDateFormatter创建DateFormatter对象耗时=\(%f)s%ld次设置日期格式耗时=\(%f)s生成字符串耗时=\(%f)s",duration1,number,duration2,duration3);
}

- (void)dateFormatterWithNOCache:(NSInteger)number {
    CFAbsoluteTime startTime1  = 0;
    CFAbsoluteTime startTime2 = 0;
    CFAbsoluteTime startTime3 = 0;
    CFAbsoluteTime startTime4 = 0;
    
    CFAbsoluteTime duration1 = 0;
    CFAbsoluteTime duration2 = 0;
    CFAbsoluteTime duration3 = 0;
    
    NSDate *datenow = [NSDate date];
    for (NSUInteger i = 0; i < number; i++) {
        startTime1 = CFAbsoluteTimeGetCurrent();
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        startTime2 = CFAbsoluteTimeGetCurrent();
        if (i%2==0) {
            [formatter setDateFormat:@"yyyy/MM/dd"];
        }else {
            [formatter setDateFormat:@"YYYY:MM:dd HH:mm:ss"];
        }
        startTime3 = CFAbsoluteTimeGetCurrent();
        [formatter stringFromDate:datenow];
        startTime4 = CFAbsoluteTimeGetCurrent();
        
        duration1 += (startTime2 - startTime1) ;
        duration2 += (startTime3 - startTime2) ;
        duration3 += (startTime4 - startTime3) ;
        
    }
    NSLog(@"不缓存NSDateFormatter%ld次创建DateFormatter对象耗时=\(%f)s设置日期格式耗时=\(%f)s生成字符串耗时=\(%f)s\n\n",number,duration1,duration2,duration3);
}

结果:

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000058)s1次设置日期格式耗时=(0.000042)s生成字符串耗时=(0.004724)s
不缓存NSDateFormatter1次创建DateFormatter对象耗时=(0.000022)s设置日期格式耗时=(0.000007)s生成字符串耗时=(0.000615)s

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000088)s10次设置日期格式耗时=(0.000060)s生成字符串耗时=(0.000954)s
不缓存NSDateFormatter10次创建DateFormatter对象耗时=(0.000065)s设置日期格式耗时=(0.000015)s生成字符串耗时=(0.003482)s

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000056)s100次设置日期格式耗时=(0.000336)s生成字符串耗时=(0.002340)s
不缓存NSDateFormatter100次创建DateFormatter对象耗时=(0.000222)s设置日期格式耗时=(0.000089)s生成字符串耗时=(0.019351)s

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000027)s1000次设置日期格式耗时=(0.000607)s生成字符串耗时=(0.003437)s
不缓存NSDateFormatter1000次创建DateFormatter对象耗时=(0.000403)s设置日期格式耗时=(0.000182)s生成字符串耗时=(0.045180)s

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000006)s10000次设置日期格式耗时=(0.002752)s生成字符串耗时=(0.015727)s
不缓存NSDateFormatter10000次创建DateFormatter对象耗时=(0.002753)s设置日期格式耗时=(0.001150)s生成字符串耗时=(0.301610)s

缓存NSDateFormatter创建DateFormatter对象耗时=(0.000009)s100000次设置日期格式耗时=(0.027292)s生成字符串耗时=(0.158854)s
不缓存NSDateFormatter100000次创建DateFormatter对象耗时=(0.027687)s设置日期格式耗时=(0.011781)s生成字符串耗时=(3.007739)s

通过数据可以看到生成字符串方法stringFromDate耗时最多。
通过Instruments中的Timer检测工具也可印证

stringFromDate.png stringFromDate.png

NSDateFormatter生成字符串方法stringFromDate耗时最多,缓存后能大幅度降低时间消耗。

问题:缓存后生成字符串方法stringFromDate耗时也相对降低???

C VS OC

分别对OC及C方法调用1、10、100、1000、10000、100000次,查看其耗时时长。

- (void)convertDateToString {
    [self convertDateToStringUsingNewDateFormatterWithNumber:1];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:1];
    [self convertDateToStringUsingCLocaltimeWithNumber:1];
    
    [self convertDateToStringUsingNewDateFormatterWithNumber:10];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:10];
    [self convertDateToStringUsingCLocaltimeWithNumber:10];
    
    [self convertDateToStringUsingNewDateFormatterWithNumber:100];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:100];
    [self convertDateToStringUsingCLocaltimeWithNumber:100];
    
    [self convertDateToStringUsingNewDateFormatterWithNumber:1000];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:1000];
    [self convertDateToStringUsingCLocaltimeWithNumber:1000];
    
    [self convertDateToStringUsingNewDateFormatterWithNumber:10000];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:10000];
    [self convertDateToStringUsingCLocaltimeWithNumber:10000];
    
    [self convertDateToStringUsingNewDateFormatterWithNumber:100000];
    [self convertDateToStringUsingCacheDateFormatterWithNumber:100000];
    [self convertDateToStringUsingCLocaltimeWithNumber:100000];
}



- (void)convertDateToStringUsingNewDateFormatterWithNumber:(NSInteger)number {
    CFAbsoluteTime then = CFAbsoluteTimeGetCurrent();
    for (NSUInteger i = 0; i < number; i++) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy/MM/dd"];
        NSDate *datenow = [NSDate date];
        [formatter stringFromDate:datenow];
    }
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    NSLog(@"不缓存NSDateFormatter%ld次生成时间字符串耗时: %f s", number, now - then);
}

- (void)convertDateToStringUsingCacheDateFormatterWithNumber:(NSInteger)number {
    CFAbsoluteTime then = CFAbsoluteTimeGetCurrent();
    _formatter= [[NSDateFormatter alloc] init];
    for (NSUInteger i = 0; i < number; i++) {
        [_formatter setDateFormat:@"yyyy/MM/dd"];
        NSDate *datenow = [NSDate date];
        [_formatter stringFromDate:datenow];
    }
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    NSLog(@"缓存NSDateFormatter%ld次生成时间字符串耗时: %f s", number, now - then);
}

- (void)convertDateToStringUsingCLocaltimeWithNumber:(NSInteger)number {
    CFAbsoluteTime then = CFAbsoluteTimeGetCurrent();
    for (NSUInteger i = 0; i < number; i++) {
        char buffer[80];
        time_t timeInterval = time(0);
        struct tm tm1;
        localtime_r(&timeInterval, &tm1);
        strftime(buffer, sizeof(buffer), "%Y年%m月%d日%H时%M分%S秒", &tm1);
        [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
    }
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    NSLog(@"localtime_r%ld次生成时间字符串耗时: %f s\n", number, now - then);
}

运行结果:

不缓存NSDateFormatter1次生成时间字符串耗时: 0.004792 s
缓存NSDateFormatter1次生成时间字符串耗时: 0.000593 s
localtime_r1次生成时间字符串耗时: 0.000144 s

不缓存NSDateFormatter10次生成时间字符串耗时: 0.004214 s
缓存NSDateFormatter10次生成时间字符串耗时: 0.000647 s
localtime_r10次生成时间字符串耗时: 0.000092 s

不缓存NSDateFormatter100次生成时间字符串耗时: 0.021961 s
缓存NSDateFormatter100次生成时间字符串耗时: 0.001064 s
localtime_r100次生成时间字符串耗时: 0.000497 s

不缓存NSDateFormatter1000次生成时间字符串耗时: 0.052935 s
缓存NSDateFormatter1000次生成时间字符串耗时: 0.001608 s
localtime_r1000次生成时间字符串耗时: 0.001124 s

不缓存NSDateFormatter10000次生成时间字符串耗时: 0.329318 s
缓存NSDateFormatter10000次生成时间字符串耗时: 0.013351 s
localtime_r10000次生成时间字符串耗时: 0.009833 s

不缓存NSDateFormatter100000次生成时间字符串耗时: 3.297733 s
缓存NSDateFormatter100000次生成时间字符串耗时: 0.132972 s
localtime_r100000次生成时间字符串耗时: 0.098528 s

结论:使用C语言方法较OC方法耗时少。

相关文章

网友评论

    本文标题:iOS 日期转换解析(四)-NSDateFormatter优化

    本文链接:https://www.haomeiwen.com/subject/dpssxhtx.html