现在的位置: 首页 > 综合 > 正文

Cocoa中NSString到NSDate的处理

2014年02月05日 ⁄ 综合 ⁄ 共 2877字 ⁄ 字号 评论关闭

NSDate是NS类库中基础类型之一。随着数字化发展,程序对数据处理量越来越大,我们经常从服务器取得的日期是字符串序列,格式化为正确的date类型是一个不可避免的工作。在Cocoa程序里提供了非常方便的函数和类,但是仍然需要我们了解一些技巧。尤其是当我们的程序面对大量的日期字符串转换的时候,要格外的注意。苹果文档中使用NSDateFormatter类格式化日期字符串,但是以防读者不知道,我这里提一下:它的速度非常慢!!这篇文章介绍如何处理这种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (NSDate *)dateFromString:(NSString *)string {
    if (!string) {
        return nil;
    }
    //Wed Mar 14 16:40:08 +0800 2012
    static NSDateFormatter *dateformatter=nil;
    if(dateformatter==nil){
        dateformatter = [[NSDateFormatter alloc] init];
        NSTimeZone *tz = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
        [dateformatter setTimeZone:tz];
        [dateformatter setDateFormat:@"EEE MMM dd HH:mm:ss Z yyyy"];
    }
    return [dateformatter dateFromString:string];
}

由于NSDateFormatter内部代码原因,所以格式化字符串代价很大。对于个别地方使用它做日期转换是非常方便的,但是如果是放在一个大的循环内部,直接使用NSDateFormatter绝对不是明智的选择。它很有可能成为拖慢你程序速度的元凶。

根据这篇博客
How to Drastically Improve Your App with an Afternoon and Instruments
编写c代码格式化时间字符串速度会提高不少。

1
2
3
4
5
6
7
8
9
10
11
12
- (NSDate *)dateFromISO8601String:(NSString *)string {
    if (!string) return nil;

    struct tm tm;
    time_t t;

    strptime([string cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &tm);
    tm.tm_isdst = -1;
    t = mktime(&tm);

    return [NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
}

其实,虽然看似使用c语言格式化时间速度很快,但实际上strptime内部仍然需要parser你的格式化字符串,该计算同样耗时。所以上述代码并不是最快的。其实如果你知道你的程序将会取得什么格式的日期字符串,那么直接字符串处理后利用NSCalendar和NSDateComponents可以更快。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (NSDate*)dateFromString:(NSString *)string;
{
    //Wed Mar 14 16:40:08 +0800 2012
    if (!string) return nil;

    static NSCalendar *gregorian=nil;
    static NSDateComponents *comps=nil;
    static NSDictionary *month=nil;
    if (gregorian==nil) {
        gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        comps = [[NSDateComponents alloc] init];
        month = [NSDictionary dictionaryWithObjectsAndKeys:
                 [NSNumber numberWithInt:1], @"Jan",
                 [NSNumber numberWithInt:2], @"Feb",
                 [NSNumber numberWithInt:3], @"Mar",
                 [NSNumber numberWithInt:4], @"Apr",
                 [NSNumber numberWithInt:5], @"May",
                 [NSNumber numberWithInt:6], @"Jun",
                 [NSNumber numberWithInt:7], @"Jul",
                 [NSNumber numberWithInt:8], @"Aug",
                 [NSNumber numberWithInt:9], @"Sep",
                 [NSNumber numberWithInt:10], @"Oct",
                 [NSNumber numberWithInt:11], @"Nov",
                 [NSNumber numberWithInt:12], @"Dec",
                 nil];
    }

    NSArray *a=[string componentsSeparatedByString:@" "];
    if ([a count]==6) {
        @try {
            [comps setYear:[[a objectAtIndex:5] intValue]];
            [comps setMonth:[[month objectForKey:[a objectAtIndex:1]] intValue]];
            [comps setDay:[[a objectAtIndex:2] intValue]];
            NSString *time=[a objectAtIndex:3];
            [comps setHour:[[time substringWithRange:NSMakeRange(0, 2)] intValue]];
            [comps setMinute:[[time substringWithRange:NSMakeRange(3, 2)] intValue]];
            [comps setSecond:[[time substringWithRange:NSMakeRange(6, 2)] intValue]];
        }
        @catch (NSException *exception) {
        }
        @finally {
        }
        return [gregorian dateFromComponents:comps];
    }
    return [NSDate date];
}

可以看到单独解析字符串的方法,虽然代码要长,但实际上它的速度是最快的。

最后作为参考资料,说明一下 OSX 10.6 下 NSDateFormatter 使用 Unicode Locale Data Markup Language (LDML)
version tr35-10 标准。作为标准文档,Apple是不会全部写到开发文档里的,不明白的同学也不用抱怨。

抱歉!评论已关闭.