2012-01-12 81 views
40

当人们认为这很贵时,他们的意思是什么?我为中间存储创建了很多瞬态对象的实例(NSString和NSDate是典型的)。我如何知道我的程序使用NSDateFormatter是否过度使用它?直到现在,我倾向于创建相当于单身人士的东西,但是我的偏好是将其封装到与它相关的一些其他对象中,所以我可以使用自引用。为什么分配或初始化NSDateFormatter被视为“昂贵”?

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

+0

分析是无痛 – justin 2012-02-04 14:11:00

+0

您可以分析您的应用程序,以确定哪种方式适合您的需要更好。我认为开发人员想指出,像NSDateFormatter这样无害的东西可能有一些非常复杂的初始化代码,所以这里提供了一个文档中的提示,以便尽可能重用初始化对象。 – 2012-02-06 15:19:02

回答

33

当像这样的东西被称为昂贵的,并不一定意味着你永远不应该这样做,这只是意味着避免在需要尽快摆脱方法的情况下做到这一点。例如,当iPhone 3G是最新的设备时,我正在编写一个带有格式化数字的应用程序,用于在每个单元格中显示(我可能会补充说,当我是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; 
} 

这段代码的滚动性能可怕。由于我每次拨打tableView:cellForRowAtIndexPath:时都会分配一个新的NSNumberFormatter,因此帧速率降至大约15 FPS。

我固定它的代码更改为此:

- (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伊娃,使tableView:cellForRowAtIndexPath:每次运行不再分配一个新的实例。这个简单的改变将滚动性能提高到大约60 FPS。

这个具体的例子不再那么重要,因为新芯片能够处理分配而不影响滚动性能,但总是尽可能高效。

+1

与此相关的危险是您必须确保您的numberFormatter不会从多个线程中调用。它不支持并发。 – 2013-09-13 22:13:33

+1

@GregMaletic这是一个非常简单的例子,''''tableView:cellForRowAtIndexPath:''''将永远不会被除了主线程以外的任何东西调用。 – 2013-09-14 22:12:09

+0

@Eli True,但是如果在代码中的另一个方法中调用numberFormatter方法,并且该方法在另一个线程上调用,那么可以使用自己的方法。你只需要记住只从主线程调用你的numberFormatter方法。 – 2013-09-15 02:04:13

7

我以前有过同样的问题。我在一些我正在使用的应用程序上运行过Instruments,我发现以前的开发人员正在为他们所做的每个自定义日志创建一个新的NSDateFormatter。由于每个屏幕他们用来记录约3条线。该应用程序只用了大约1秒钟才创建NSDateFormatters。

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

经过一些微不足道的想法之后,我带着一个“工厂”来处理NSDateFormatters的重用,它基于想要的格式和语言环境。我请求某种格式和语言环境的日期格式化程序,我的课程将已经加载的格式化程序给我。良好的性能调整,你应该尝试。

PS:也许有人想对它进行测试,所以我把它公开:https://github.com/DougFischer/DFDateFormatterFactory/blob/master/README.md

+1

你有没有得到任何反馈? – Dejell 2013-09-17 16:29:36

+1

反馈:作品像一个魅力 – Laszlo 2013-10-15 09:30:34

+1

我同意。简单方便。我目前的应用程序使用它有很多好处。非常感谢 – rmvz3 2014-07-29 02:47:24