25

[性能优化]DateFormatter深度优化探索

 5 years ago
source link: http://www.cocoachina.com/ios/20190108/26062.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

在iOS开发中,对日期进行格式化处理通常有三个步骤:

  • 创建DateFormatter对象

  • 设置日期格式

  • 使用DateFormatter对象对日期进行处理

在上篇文章 《DateFormatter性能优化》 中,我们通过创建单例对象的方式对创建DateFormatter对象,设置日期格式两个步骤进行了缓存,将方法耗时降低为不缓存的方案的10%左右,但是这种优化方法受制于DateFormatter的几个系统方法的执行效率,本身具有一定的局限性。之前在一些文章中,也看到了使用C语言的

size_t     strftime_l(char * __restrict, size_t, const char * __restrict,
        const struct tm * __restrict, locale_t)
        __DARWIN_ALIAS(strftime_l) __strftimelike(3);

函数对日期格式化进行处理,所以本文将对以下几种情况的方法耗时进行评测:

  • 使用Objective-C,不缓存DateFormatter对象

  • 使用Objective-C,缓存DateFormatter对象

  • 使用Objective-C,调用strftime_l做日期处理

  • 使用Swift,不缓存DateFormatter对象

  • 使用Swift,缓存DateFormatter对象

  • 使用Swift,调用strftime_l做日期处理

Objective-C的三种情况下的代码

//不缓存DateFormatter对象
-(void)testDateFormatterInOCWithoutCache:(NSInteger)times {
NSString *string = @"";
NSDate *date;
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
for (int i=0; i
NSDateFormatter *dateFormatter = [[ NSDateFormatter alloc] init];
[dateFormatter setDateFormat: @"yyyy年MM月dd日HH时mm分ss秒"];
date = [ NSDate dateWithTimeIntervalSince1970:( 1545308405 + i)];
string = [dateFormatter stringFromDate:date];
}
CFAbsoluteTime duration = ( CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
NSLog( @"\n不缓存DateFormatter对象的方案:\n计算%ld次\n耗时%f ms\n", ( long)times, duration);
}

//缓存DateFormatter对象
-( void)testDateFormatterInOCWithCache:( NSInteger)times {
NSString *string = @"";
NSDate *date;
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
for ( int i= 0; i
date = [ NSDate dateWithTimeIntervalSince1970:( 1545308405 + i)];
string = [[DateFormatterCache shareInstance].formatterOne stringFromDate:date];
}
CFAbsoluteTime duration = ( CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
NSLog( @"\n缓存DateFormatter对象的方案:\n计算%ld次\n耗时%f ms\n", ( long)times, duration);
}

//使用C语言来做日期处理
-( void)testDateFormatterInC:( NSInteger)times {
NSString *string = @"";
NSDate *date;
time_t timeInterval;
char buffer[ 80];
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
for ( int i= 0; i
date = [ NSDate dateWithTimeIntervalSince1970:( 1545308405 + i)];
timeInterval = [date timeIntervalSince1970];
strftime(buffer, sizeof(buffer), "%Y年%m月%d日%H时%M分%S秒", localtime(&timeInterval));
string = [ NSString stringWithCString:buffer encoding: NSUTF8StringEncoding];
}
CFAbsoluteTime duration = ( CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
NSLog( @"==%@", string);
NSLog( @"\n使用C语言的方案:\n计算%ld次\n耗时%f ms\n", ( long)times, duration);
}

附上tm结构体:

struct tm {
    int tm_sec;     /* seconds after the minute [0-60] */
    int tm_min;     /* minutes after the hour [0-59] */
    int tm_hour;    /* hours since midnight [0-23] */
    int tm_mday;    /* day of the month [1-31] */
    int tm_mon;     /* months since January [0-11] */
    int tm_year;    /* years since 1900 */
    int tm_wday;    /* days since Sunday [0-6] */
    int tm_yday;    /* days since January 1 [0-365] */
    int tm_isdst;   /* Daylight Savings Time flag */
    long    tm_gmtoff;  /* offset from UTC in seconds */
    char    *tm_zone;   /* timezone abbreviation */
};

Swift三种情况下的代码

    //不进行缓存
func testInOldWay(_ times: Int) {
var string = ""
var date = Date.init()
let startTime = CFAbsoluteTimeGetCurrent();
for i in 0..
let formatter = DateFormatter()
formatter.dateFormat = "yyyy年MM月dd日HH时mm分ss秒"
date = Date.init(timeIntervalSince1970: TimeInterval( 1545308405 + i))
string = formatter. string( from: date)
}
let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
print( "使用oldWay计算\n\(times)次,总耗时\n\(duration) ms\n")
}
//进行缓存
func testInNewWay(_ times: Int) {
var string = ""
var date = Date.init()
let startTime = CFAbsoluteTimeGetCurrent();
for i in 0..
date = Date.init(timeIntervalSince1970: TimeInterval( 1545308405 + i))
string = DateFormatterCache.shared.dateFormatterOne. string( from: date)
}
let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
print( "使用缓存Formatter的方案计算\n\(times)次,总耗时\n\(duration) ms\n")
}

//使用C语言来做日期处理
func testFormatterInC(_ times: Int) {
var date = Date.init()
var dateString = ""
var buffer = [Int8](repeating: 0, count: 100)
var time = time_t(date.timeIntervalSince1970)
let format = "%Y年%m月%d日%H时%M分%S秒"
let startTime = CFAbsoluteTimeGetCurrent();
for i in 0..
date = Date.init(timeIntervalSince1970: TimeInterval( 1545308405 + i))
time = time_t(date.timeIntervalSince1970)
strftime(&buffer, buffer.count, format, localtime(&time))
dateString = String.init(cString: buffer, encoding: String.Encoding.utf8) ?? ""
}
let duration = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
print( "使用C语言的方案计算\n\(times)次,总耗时\n\(duration) ms\n")
print(dateString)
}

iOS 12.1 iPhone 7

测试结果:

MR3MJzr.jpg!web

测试结果:

在Objective-C中, 不使用缓存,使用缓存,使用C语言函数处理的耗时比约为100:10.7:3.5

在Swift中, 不使用缓存,使用缓存,使用C语言函数处理的耗时比约为100:11.7:6.6

Swift在使用DateFormatter进行处理时,不论是缓存的方案还是不缓存的方案,跟使用Objective-C的耗时基本一致,而在Swift中使用C语言的函数来做日期处理时,时间约为使用Objective-C的两倍,而且当只做一次日期处理时,由于涉及到一些初始资源的初始化,所以看上去比后面执行10次的时间还多

最后

如果项目是Objective-C的项目,我觉得可以采用这种C语言的strftime来做日期处理,能将时间降低为缓存NSDateFormatter的方案的33%左右,如果是Swift项目,调用C语言函数的效率没有在Objective-C项目中那么高,虽然能将时间降低为缓存NSDateFormatter的方案的56%左右,但是在Swift中使用C语言的函数存在一定的风险,在这里风险之一就是 time = time_t(date.timeIntervalSince1970) 这行代码返回的值是time_t类型,time_t类型的定义如下:

public typealias time_t = __darwin_time_t
public typealias __darwin_time_t = Int /* time() */

time_t其实就是Int,当Swift项目运行在32位设备(也就是iphone 5,iphone 5C)上时,Int类型是32位的,最大值为2147483647,如果这是一个时间戳的值,转换为正常时间是2038-01-19 11:14:07,也就是处理的时间是未来的日期,2038年以后的话,会出现数值溢出。

Demo在这里: github.com/577528249/S…

参考资料:

forums.developer.apple.com/thread/2905… stackoverflow.com/questions/2…

作者:Not_Found

链接:https://juejin.im/post/5c1b69dde51d45199c387a73


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK