为什么分配或初始化NSDateFormatter被认为是“昂贵的”?

时间:2012-01-12 09:34:10

标签: ios nsdateformatter

当人们说这很贵时,人们的意思是什么?我为中间存储创建了许多瞬态对象的实例(NSString和NSDate是典型的)。我怎么知道我的程序使用NSDateFormatter是否过度了?

到目前为止,我倾向于创建相当于单身的东西,但我的偏好是将其封装到与之关联的其他一些对象中,这样我就可以使用自引用。

没有运行性能测试,我正在寻找一个更好的“经验法则”理解为什么我应该或不应该这样做。

2 个答案:

答案 0 :(得分:38)

当这样的事情被称为昂贵时,并不一定意味着你永远不应该这样做,它只是意味着在你需要尽快摆脱方法的情况下避免这样做。例如,当iPhone 3G是最新的设备时,我正在编写一个带有UITableView的应用程序,格式化的数字可以显示在每个单元格中(我可能会补充说,这是我在iOS开发时的初学者) 。我的第一次尝试如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    NSString *reuseIdentifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

    MyManagedObject *managedObject = [self.managedObjects objectAtIndex:indexPath.row];
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

    [cell.textLabel setText:[managedObject title]];
    [cell.detailTextLabel setText:[numberFormatter stringFromNumber:[managedObject amount]]];

    return cell;
}

此代码的滚动性能很糟糕。帧速率降至约15 FPS,因为每次NSNumberFormatter命中时我都会分配一个新的tableView:cellForRowAtIndexPath:

我通过将代码更改为:

来修复它
- (NSNumberFormatter *)numberFormatter {

    if (_numberFormatter != nil) {
        return _numberFormatter;
    }

    _numberFormatter = [[NSNumberFormatter alloc] init];
    [_numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

    return _numberFormatter;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    NSString *reuseIdentifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

    MyManagedObject *managedObject = [self.managedObjects objectAtIndex:indexPath.row];
    NSNumberFormatter *numberFormatter = [self numberFormatter];

    [cell.textLabel setText:[managedObject title]];
    [cell.detailTextLabel setText:[numberFormatter stringFromNumber:[managedObject amount]]];

    return cell;
}

这里的区别在于我已经懒惰地将NSNumberFormatter加载到ivar中,因此tableView:cellForRowAtIndexPath:的每次运行都不再分配新实例。这个简单的更改将滚动性能恢复到大约60 FPS。

这个具体的例子不再那么相关了,因为较新的芯片能够在不影响滚动性能的情况下处理分配,但最好尽可能提高效率。

答案 1 :(得分:7)

前一段时间我有同样的问题。我在一些我工作的应用程序上运行了Instruments,我发现以前的开发人员正在为他们所做的每个自定义日志创建一个新的NSDateFormatter。因为对于每个屏幕,他们用来记录大约3行。该应用程序过去花费大约一秒时间才创建NSDateFormatters。

简单的解决方案是将日期格式化程序实例作为属性或类别保留在类中,并将其重用于每个日志行。

经过一些微不足道的思考之后,我来到了一个“工厂”来处理基于格式和所需区域设置的NSDateFormatters的重用。我请求一些格式和语言环境的日期格式化程序,我的类给我已经加载的格式化程序。良好的性能调优,你应该尝试。

PS:也许有人想测试它,所以我把它公之于众:https://github.com/DougFischer/DFDateFormatterFactory/blob/master/README.md