Joey Xu

大浪淘沙 沉者为金

Hi, this is Joey, an iOS developer. Welcome to my website!


NSDateFormatter最佳实践

本文介绍NSDateFormatter的性能瓶颈,以及如何解决性能问题。


分别用NSDateFormatter和C的localtime()方法去将时间转化成一个可读的字符串。转化1024*10次,然后对比时间。
注:localtime()有不可重入和线程安全的问题,可用localtime_r替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (void)viewDidLoad {
[super viewDidLoad];
    
[self convertDateToStringUsingNewDateFormatter];
    
[self convertDateToStringUsingCLocaltime];
}

- (void)convertDateToStringUsingNewDateFormatter {
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < ITERATIONS; i++) {
NSDateFormatter *newDateForMatter = [[NSDateFormatter alloc] init];
[newDateForMatter setTimeZone:self.timeZone];
[newDateForMatter setDateFormat:@"yyyy-MM-dd"];
self.dateAsString = [newDateForMatter stringFromDate:[NSDate date]];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"Convert date to string using NSDateFormatter costs time: %f seconds!\n", now - then);
}

- (void)convertDateToStringUsingCLocaltime {
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < ITERATIONS; i++) {
time_t timeInterval = [NSDate date].timeIntervalSince1970;
struct tm *cTime = localtime(&timeInterval);
self.dateAsString = [NSString stringWithFormat:@"%d-%02d-%02d", cTime->tm_year + 1900, cTime->tm_mon + 1, cTime->tm_mday];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"Convert date to string using C localtime costs time: %f seconds!\n", now - then);
}

在控制台可以看到输出结果:

1
2
Convert date to string using NSDateFormatter costs time: 4.289286 seconds!
Convert date to string using C localtime() costs time: 0.038355 seconds!

使用NSDateFormatter耗时4.289286秒,
使用localtime耗时0.038355秒。
也就是说,NSDateFormatter要比localtime慢100倍+

用instruments跑了一下time profile,
time profile
可以看到这性能差距之大。

为啥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的过程开销很大!建议使用时保持一个单例,而不是每次去重新创建
NSDateFormatter对象。

demo中加入一个单例NSDateFormatter的实现方法,

1
2
3
4
5
6
7
8
- (void)convertDateToStringUsingSingletonFormatter {
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < ITERATIONS; i++) {
self.dateAsString = [self.dateFormatter stringFromDate:[NSDate date]];
}
now = CFAbsoluteTimeGetCurrent();
NSLog(@"Convert date to string using Singleton Formatter costs time: %f seconds!\n", now - then);
}

控制台输出,

1
2
3
Convert date to string using NSDateFormatter costs time: 4.231905 seconds!
Convert date to string using Singleton Formatter costs time: 0.031584 seconds!
Convert date to string using C localtime() costs time: 0.039877 seconds!

嗯,若使用单例,NSDateFormatter开销和localtime()的开销差不多,甚至会稍快一些。

综上,最佳实践应该是,在工程中添加一个NSDateFormatter的单例对象供全工程使用,需要注意的是,NSDateFormatter在iOS7之后(包括iOS7)才是线程安全的。


Demo地址

NSDateFormatter文档

Newer Post

如何安全使用dispatch_sync

概述iOS开发者在与线程打交道的方式中,使用最多的应该就是GCD框架了,没有之一。GCD将繁琐的线程抽象为了一个个队列,让开发者极易理解和使用。但其实队列的底层,依然是利用线程实现的,同样会有死锁的问题。本文将探讨如何规避disptach_sync接口引入的死锁问题。 GCD基础GCD最基础的两个 …

Tech 继续阅读
Older Post

选择 GCD 还是 NSTimer ?

我们常常会延迟某件任务的执行,或者让某件任务周期性的执行。然后也会在某些时候需要取消掉之前延迟执行的任务。 延迟操作的方案一般有三种: 1. NSObject的方法:- (void)performSelector:(SEL)aSelector withObject:(nullable id)anAr …

Tech 继续阅读