When something like this is referred to as expensive, it doesn't necessarily mean that you should never do it, it just means avoid doing it in situations when you need to get out of a method as quickly as possible. For example, back when the iPhone 3G was the latest device, I was writing an application with a UITableView
that formatted numbers for display in each cell (I might add, this was back when I was a beginner at iOS development). My first attempt was the following:
- (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;
}
The scrolling performance of this code was terrible. The frame rate dropped to about 15 FPS because I was allocating a new NSNumberFormatter
every time tableView:cellForRowAtIndexPath:
was hit.
I fixed it by changing the code to this:
- (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;
}
The difference here is that I've lazily loaded the NSNumberFormatter
into an ivar, so that each run of tableView:cellForRowAtIndexPath:
no longer allocates a new instance. This simple change pushed the scrolling performance back up to about 60 FPS.
This specific example isn't quite as relevant anymore, as the newer chips are capable of handling the allocation without affecting scrolling performance, but it's always better to be as efficient as possible.