Cocoa in the Shell

NSDateFormatter performances

In this post I talk about NSDateFormatter and how to use it correctly in your applications, especially iPhone ones, because a misuse of it can cause serious performances issues.

NSDateFormatter instances are used to create string representations of NSDate objects or to convert NSDate objects to textual representation, so it’s very common to use it.

To illustrate this I’ll create a simple Navigation-based iPhone application, which has a UITableView. I will populate this tableView with 100 cells containing a formatted date based on a UNIX timestamp.
In the first test I’ll create a new NSDateFormatter for each cell. In the second I’ll put the NSDateFormatter as an ivar and initialize it once, and we will look at the difference of performance with Instruments.

1st method

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    static NSString* CellIdentifier = @"MyCell";

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell)
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    NSDate* date = [NSDate date]; // get current date

    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"dd/MM' - 'HH'h'mm"];
    cell.textLabel.text = [formatter stringFromDate:date];
    [formatter release];

    return cell;
}

2nd method

-(void)viewDidLoad
{
  _formatter = [[NSDateFormatter alloc] init]; // _formatter is the NSDateFormatter ivar
  [_formatter setDateFormat:@"dd/MM' - 'HH'h'mm"];
}

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    static NSString* CellIdentifier = @"MyCell";

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell)
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    NSDate* date = [NSDate date]; // get current date

    cell.textLabel.text = [_formatter stringFromDate:date];

    return cell;
}

Now that we have our two examples, let’s compare with the Time Profiler Instrument.
In Xcode I chose Run with Performance Tool and select the Time Profiler template, by default this template isn’t available via Xcode, you have to launch Instruments, select it and then chose Save As Template.

The test is really simple, once the application launched, I scroll to the bottom of the tableView and go back to the top, then stop the test.
To filter the quantity of informations, check Hide Missing Symbols and Hide System Libraries.
The line which interests us is the one containing the call to cellForRowAtIndexPath:. Now let’s compare the results with the 2 methods presented above.

1st

2nd

I don’t think there is much to say here, we clearly see that the first method is much much slower.

Why is that ?

The explication is very simple, each time you create a NSDateFormatter the expression need to be compiled, and it takes time, lot of time.

So the conclusion is, avoid to create formatters in loops or tableView delegate where you are supposed to hold lot of data because it’s a time killer.

Just instantiate one formatter that you use the time your application live, and release it when you are done.