2012-01-12 16 views
40

¿Qué quiere decir la gente cuando dice que es caro? Creo instancias de muchos objetos transitorios solo para el almacenamiento intermedio (NSString y NSDate son los típicos). ¿Cómo puedo saber si el uso de NSDateFormatter en mi programa es exagerado?¿Por qué asignar o inicializar NSDateFormatter se considera "costoso"?

Hasta ahora, he tendido a crear lo que equivale a un singleton, pero mi preferencia sería encapsularlo en algunos de los otros objetos a los que está asociado para que pueda usar las referencias propias.

A falta de pruebas de rendimiento en ejecución, estoy buscando una mejor comprensión de por qué debería o no hacer esto.

+0

perfiles es indoloro – justin

+0

Puede perfil de su aplicación para determinar de qué manera se adapte mejor a sus necesidades. Creo que los desarrolladores querían señalar que algo tan inocuo como 'NSDateFormatter' puede tener un código de inicializador bastante complicado, así que aquí hay una pista en la documentación para reutilizar el objeto inicializado tanto como sea posible. –

Respuesta

33

Cuando algo así se conoce como costoso, no significa necesariamente que nunca deba hacerlo, simplemente significa evitar hacerlo en situaciones en las que necesita salir de un método lo más rápido posible. Por ejemplo, cuando el iPhone 3G era el último dispositivo, estaba escribiendo una aplicación con un UITableView que formateaba los números para mostrar en cada celda (podría agregar, esto fue cuando yo era un principiante en el desarrollo de iOS). Mi primer intento fue la siguiente:

- (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; 
} 

El rendimiento de desplazamiento de este código fue terrible. La velocidad de fotogramas bajó a aproximadamente 15 FPS porque estaba asignando un nuevo NSNumberFormatter cada vez que se golpeó tableView:cellForRowAtIndexPath:.

me fijo al cambiar el código para esto:

- (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; 
} 

La diferencia aquí es que me he cargado con pereza el NSNumberFormatter en una Ivar, de manera que cada ejecución de tableView:cellForRowAtIndexPath: ya no se asigna una nueva instancia. Este simple cambio empujó el rendimiento de desplazamiento hasta unos 60 FPS.

Este ejemplo específico ya no es tan relevante, ya que los chips más nuevos son capaces de manejar la asignación sin afectar el rendimiento del desplazamiento, pero siempre es mejor ser lo más eficiente posible.

+1

El peligro con esto es que debe asegurarse de que su numberFormatter no se llame desde más de un hilo. No admite concurrencia. –

+1

@GregMaletic este es un ejemplo muy simple, '' 'tableView: cellForRowAtIndexPath:' '' 'nunca se llamará desde cualquier cosa que no sea el hilo principal. –

+0

@Eli Es cierto, pero si su método numberFormatter es llamado en otro método en su código, y ese método fue invocado en otro hilo, podría atornillarse. Solo debe recordar llamar solo a su método numberFormatter desde el hilo principal. –

7

Tuve esta misma pregunta hace algún tiempo. Ejecuté Instruments en alguna aplicación en la que estaba trabajando y descubrí que los desarrolladores anteriores estaban creando un nuevo NSDateFormatter para cada registro personalizado que hacían. Dado que para cada pantalla solían registrar aproximadamente 3 líneas. La aplicación solía gastar alrededor de un segundo solo creando NSDateFormatters.

La solución simple sería retener la instancia del formateador de fecha en su clase como un atributo o algo así y reutilizarlo para cada línea de registro.

Después de algunas ideas triviales, he venido con una "fábrica" ​​para manejar la reutilización de NSDateFormatters según el formato y el entorno deseado. Solicito un formateador de fecha de algún formato y locale y mi clase me da el formateador ya cargado. Buen ajuste de rendimiento, deberías intentarlo.

PS: Tal vez alguien le gustaría probarlo, así que lo hizo público: https://github.com/DougFischer/DFDateFormatterFactory/blob/master/README.md

+1

¿Recibió algún comentario? – Dejell

+1

Comentarios: funciona como un encanto – Laszlo

+1

Estoy de acuerdo. Fácil y conveniente. Mi aplicación actual tiene muchos beneficios al usarla. Muchas gracias – rmvz3

Cuestiones relacionadas