在iOS開發中,對日期進行格式化處理一般有三個步驟:git
在上篇文章《DateFormatter性能優化》中,咱們經過建立單例對象的方式對建立DateFormatter對象,設置日期格式兩個步驟進行了緩存,將方法耗時下降爲不緩存的方案的10%左右,可是這種優化方法受制於DateFormatter的幾個系統方法的執行效率,自己具備必定的侷限性。以前在一些文章中,也看到了使用C語言的github
size_t strftime_l(char * __restrict, size_t, const char * __restrict, const struct tm * __restrict, locale_t) __DARWIN_ALIAS(strftime_l) __strftimelike(3);
函數對日期格式化進行處理,因此本文將對如下幾種狀況的方法耗時進行評測:swift
//不緩存DateFormatter對象 -(void)testDateFormatterInOCWithoutCache:(NSInteger)times { NSString *string = @""; NSDate *date; CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); for (int i=0; i<times; 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<times; 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<times; 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); }
這裏對於我們iOS開發的同窗來講比較陌生的就是strftime_l(buffer, sizeof(buffer), "%Y年%m月%d日%H時%M分%S秒", localtime(&timeInterval), NULL);
這這行代碼的調用,strftime_l函數接受四個參數,第一個參數buffer是C語言中字符數組用於存儲日期格式化後的字符串,第二個參數是寫入buffer數組的最大值,若是格式化的字符串大於這個值,那麼只會取字符串的的一部分,第三個參數"%Y年%m月%d日%H時%M分%S秒"是日期格式,第四個參數localtime(&timeInterval)是指向使用當地時區對時間戳處理獲得tm類型結構體的指針數組
附上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 */ };
//不進行緩存 func testInOldWay(_ times: Int) { var string = "" var date = Date.init() let startTime = CFAbsoluteTimeGetCurrent(); for i in 0..<times { 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..<times { 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..<times { 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性能優化
測試結果:微信
測試結果:app
在Objective-C中,
不使用緩存,使用緩存,使用C語言函數處理的耗時比約爲100:10.7:3.5iphone
在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在這裏:
https://github.com/577528249/...
參考資料:
https://forums.developer.appl...
https://stackoverflow.com/que...
PS:
建了一個技術氛圍較爲濃厚的微信羣,歡迎更多對技術有熱情的同窗加入,能夠加我微信,我拉你們進羣。