正确的NSCalendar初始化

问题描述 投票:0回答:1

我们的应用在日期方面的工作相当多,但目前我们只支持格里高利历,一个应用范围内的NSCalendar实例的初始化如下。

NSCalendar *appCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];

上述方法的文档中写道 "返回的日历默认为当前地区和默认时区" 然而,当在区域设置为 "United Kingdom "的设备上运行应用时,调用 [appCalendar firstWeekday] 返回值为1(周日),而不是预期的2(周一)。如果我运行 [[NSCalendar currentCalendar] firstWeekday]在 "appCalendar "上,返回了正确的值2。起初我以为 "appCalendar "上可能没有设置locale,但记录发现它有,尽管它缺少国家代码等,而 "currentCalendar "实例有,这使得它可以返回正确的第一周日。

是否应该明确地在返回的对象上设置locale?calendarWithIdentifier 如果有,这样做有什么注意事项吗?

最新情况

根据下面zrzka的回答,我建议在初始化一个带有标识符的日历时,显式地设置一个locale,例如:。

NSCalendar *appCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];

appCalendar.locale = [NSLocale currentLocale];
ios objective-c nscalendar
1个回答
2
投票

文件 是错误的。

返回的日历默认为当前地区和默认时区。

应该是。

返回的日历默认为系统所在地区和默认时区。

CFCalendar.c:

CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) {
    if (allocator == NULL) allocator = __CFGetDefaultAllocator();
    __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
    __CFGenericValidateType(identifier, CFStringGetTypeID());
    // return NULL until Chinese calendar is available
    if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) {
//    if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
    if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
    else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
    else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
    else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
    else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
    else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
//  else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
    else return NULL;
    }
    struct __CFCalendar *calendar = NULL;
    uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase);
    calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL);
    if (NULL == calendar) {
    return NULL;
    }
    calendar->_identifier = (CFStringRef)CFRetain(identifier);
    calendar->_locale = NULL;
    calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem());
    calendar->_tz = CFTimeZoneCopyDefault();
    calendar->_cal = NULL;
    return (CFCalendarRef)calendar;
}

_locale 被初始化为 NULL_localeID 被初始化为系统的locale标识符(在iPhone & simulator上是一个空字符串)。_cal 被设置为 NULL.

CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) {
    CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, firstWeekday);
    __CFGenericValidateType(calendar, CFCalendarGetTypeID());
    if (!calendar->_cal) __CFCalendarSetupCal(calendar);
    if (calendar->_cal) {
    return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
    }
    return -1;
}

所以,因为 _calNULL, __CFCalendarSetupCal 是叫。

static void __CFCalendarSetupCal(CFCalendarRef calendar) {
    calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
}

哪些调用 __CFCalendarCreateUCalendar_localeID 是一个空字符串。

我可以在iOS 11, 12 & 13上确认这个行为。 源代码是一个叫做 CF-Lite但我进一步拆解了实际的CoreFoundation框架,它做了同样的事情......

call       _CFLocaleGetSystem             ; _CFLocaleGetSystem
mov        rdi, rax                       ; argument "cf" for method _CFRetain
call       _CFRetain                      ; _CFRetain
mov        qword [r15+0x18], rax
call       _CFTimeZoneCopyDefault         ; _CFTimeZoneCopyDefault
mov        qword [r15+0x20], rax
mov        rbx, qword [r15+0x10]
mov        rdi, qword [r15+0x18]          ; argument "locale" for method _CFLocaleGetIdentifier
call       _CFLocaleGetIdentifier         ; _CFLocaleGetIdentifier
mov        rdx, qword [r15+0x20]          ; argument #3 for method ___CFCalendarCreateUCalendar
mov        rdi, rbx                       ; argument #1 for method ___CFCalendarCreateUCalendar
mov        rsi, rax                       ; argument #2 for method ___CFCalendarCreateUCalendar
call       ___CFCalendarCreateUCalendar   ; ___CFCalendarCreateUCalendar

...使用一个空的标识符从 CFLocaleGetIdentifierCFLocaleGetSystem.

当你检查 CFCalendarCreateWithIdentifier 文档中,对当前的地域、时区......只字未提。

更有趣的是这两个方法的区别(讨论部分)。

但是没有什么区别。calendarWithIdentifier: 只是调用 alloc & initWithCalendarIdentifier:.

push       rbp
mov        rbp, rsp
push       r14
push       rbx
mov        rbx, rdx
mov        rsi, qword [0x3cb478]                       ; argument "selector" for method _objc_msgSend, @selector(alloc)
mov        r14, qword [_objc_msgSend_390220]           ; _objc_msgSend_390220
call       r14                                         ; Jumps to 0x553ae0 (_objc_msgSend), _objc_msgSend
mov        rsi, qword [0x3cc768]                       ; argument "selector" for method _objc_msgSend, @selector(initWithCalendarIdentifier:)
...

我相信这是一个文档问题,应该向苹果公司报告(做到了。FB7740798).

© www.soinside.com 2019 - 2024. All rights reserved.