4

I use the following code to create an NSDate from a specific format of an NSString.

+ (NSDateFormatter *)serverFormatter
{
    static NSDateFormatter *formatter = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM-dd'T'HHmmssZ"];
    });

    return formatter;
}

+ (NSDate *)dateFromServerDateString:(NSString *)dateString
{
    if (!dateString) {
        return nil;
    }

    NSDateFormatter *formatter = [NSDate serverFormatter];
    NSString *cleaned = [dateString stringByReplacingOccurrencesOfString:@":"
                                                           withString:@""];

    return [formatter dateFromString:cleaned];
}

Every once in a blue moon I'll receive an EXC_BAD_ACCESS on return [formatter dateFromString:cleaned]; Viewing the stack trace shows that cleaned, dateString, and formatter all have have their correct values (not nil and 'po' prints a correct value in the console).

After doing some reading, I did discover that my code needs to take into consideration thread safety on NSDateFormatter. (And I have appropriately adjusted my code which is not shown here.) However according to the stack traces, only one thread is accessing my NSDateFormatter when the crash occurred.

The stack trace with the exception looks as shown below.

#0  0x02d09ad1 in typeinfo name for icu::UObject ()
#1  0x02b8f5ed in icu::TimeZone::getOffset(double, signed char, int&, int&, UErrorCode&) const ()
#2  0x02b95652 in icu::Calendar::computeTime(UErrorCode&) ()
#3  0x02b95552 in icu::Calendar::updateTime(UErrorCode&) ()
#4  0x02b96036 in icu::Calendar::getTimeInMillis(UErrorCode&) const ()
#5  0x02c4244f in icu::DateFormat::parse(icu::UnicodeString const&, icu::ParsePosition&) const ()
#6  0x02cebd05 in udat_parse ()
#7  0x029b7161 in CFDateFormatterGetAbsoluteTimeFromString ()
#8  0x029b6f8e in CFDateFormatterCreateDateFromString ()
#9  0x0214e7e9 in getObjectValue ()
#10 0x0214e701 in -[NSDateFormatter getObjectValue:forString:errorDescription:] ()
#11 0x0214ea3f in -[NSDateFormatter dateFromString:] ()
#12 0x0001a7a4 in +[NSDate(RDUtilities) dateFromServerDateString:]
...

Has anyone encountered anything like this before and has any tips for debugging?

Awesome-o
  • 2,002
  • 1
  • 26
  • 38

1 Answers1

0

Debugging approach would be like this:

+ (NSDate *)dateFromServerDateString:(NSString *)dateString
{
    if (!dateString) {
        return nil;
    }
    NSLog (@"%@", dateString);
    NSDateFormatter *formatter = [NSDate serverFormatter];
    NSString *cleaned = [dateString stringByReplacingOccurrencesOfString:@":"
                                                           withString:@""];
    NSLog (@"%@", cleaned);  //see if cleaned represents proper string with separators
    NSLog (@"%@", formatter); //not null?

    return [formatter dateFromString:cleaned];
}

Note that your format must comply with the one described here. For further reference, read this.

UPDATE:

Objects like NSDateFormatter are best kept singleton. And that is what your original objective seems to be - from your static allocator and synchronized allocation. When you order it to create new object every time with sync block, probably cocoa is messing up things.

Here is how you do it:

+ (NSDateFormatter *)serverFormatter
{
    static NSDateFormatter *formatter = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      if (formatter == nil) {
        formatter = [[NSDateFormatter alloc] init];
      }
        [formatter setDateFormat:@"yyyy-MM-dd'T'HHmmssZ"];
    });

    return formatter;
}
Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
  • Good starting point, but, none of the values are nil and formatter complies to a proper format. This crash oddly shows up only once a week or so. I caught it today and checked all of that from the console. Even more interesting, I typed 'po [formatter dateFromString:cleaned]' into the console and it returned the output that the caller was originally expecting. – Awesome-o Aug 19 '13 at 06:23
  • Strange. Try NSZombie? – Nirav Bhatt Aug 19 '13 at 06:24
  • 2
    The check for nil is useless because dispatch_once makes sure that this block is called exactly once. And because of this, each time the block is called, formatter will be nil. – Matthias Bauch Aug 19 '13 at 06:39
  • Missed that, I was led to think somehow that you were using sync block instead of dispatch_once. So do you get a crash even when it thee formatter is valid and not nil? – Nirav Bhatt Aug 19 '13 at 06:41
  • Yup, formatter is valid and is not nil and the crash occurs. Looking here http://stackoverflow.com/questions/4944885/nsdateformatter-crash there does seem to be some familiarity between that person's stack trace and mine, so maybe it is a synchronization issue after all. The only thing that bugs me is I checked every thread's stack trace and no extra threads appear to be accessing formatter or NSDateFormatter at all when the crash occurs.. – Awesome-o Aug 19 '13 at 23:27