当人们说这很贵时,他们是什么意思?我创建了许多瞬态对象的实例,只是为了中间存储(NSString和NSDate是典型的)。如何知道我的程序是否过度使用了NSDateFormatter?
直到现在,我倾向于创建一个类似单例的东西,但我更喜欢将其封装到与之相关的其他对象中,以便我可以使用self引用。
除了运行性能测试外,我正在寻找更好的“经验法则”来理解我应该或不应该这样做的原因。
当人们说这很贵时,他们是什么意思?我创建了许多瞬态对象的实例,只是为了中间存储(NSString和NSDate是典型的)。如何知道我的程序是否过度使用了NSDateFormatter?
直到现在,我倾向于创建一个类似单例的东西,但我更喜欢将其封装到与之相关的其他对象中,以便我可以使用self引用。
除了运行性能测试外,我正在寻找更好的“经验法则”来理解我应该或不应该这样做的原因。
当某些操作被称为代价昂贵时,并不意味着你永远都不能这样做,而是应该在需要尽快退出方法的情况下避免这样做。例如,在 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;
}
这段代码的滚动性能非常差。由于我每次点击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。
虽然最新的芯片能够处理分配而不影响滚动性能,但尽可能高效总是更好的。
我曾经有过同样的问题。我在一些应用程序上运行了Instruments,并发现之前的开发人员为每个自定义日志创建了一个新的NSDateFormatter。由于他们用于记录每个屏幕大约3行,因此该应用程序只花费了约1秒钟来创建NSDateFormatters。
简单的解决方案是将日期格式化程序实例保留在您的类中作为属性或其他内容,并将其重用于每个日志行。
经过一些琐碎的思考,我想到了一个“工厂”,以根据所需的格式和区域设置重复使用NSDateFormatters。我请求某种格式和语言环境的日期格式化程序,我的类会将已加载的格式化程序给我。优秀的性能调整,你应该尝试。
PS:也许有人想测试它,所以我将其公开了:https://github.com/DougFischer/DFDateFormatterFactory/blob/master/README.md
NSDateFormatter
这样听起来无害的东西也可能有一些相当复杂的初始化代码,所以文档中提供了一个提示,尽可能重复使用已初始化的对象。 - Tim Reddy