¿Está almacenando en caching una buena idea para toda la aplicación NSDateformatter?

Es bien sabido que crear NSDateFormatters es ' costoso '

Incluso la Guía de formatting de datos de Apple (actualizada 2014-02) dice:

Crear un formatting de date no es una operación barata. Si es probable que utilice un formateador con frecuencia, generalmente es más eficiente almacenar en caching una sola instancia que crear y disponer de varias instancias. Un enfoque es usar una variable estática.

Pero ese documento parece no estar realmente actualizado con Swift y tampoco puedo encontrar nada al respecto en la Referencia de class NSDateFormatter más reciente sobre el almacenamiento en caching del formateador, así que solo puedo asumir que es tan caro para Swift como para Object-C .

Muchas fonts sugieren almacenar en caching el formateador dentro de la class que lo usa, por ejemplo, un controller o una vista.

Me preguntaba si sería útil o incluso "más barato" agregar una class singleton al proyecto para almacenar el datepicker, así que está seguro de que nunca es necesario crearlo nuevamente. Esto podría usarse en todas partes de la aplicación. También podría crear varias instancias compartidas que contienen varios datepickers. Por ejemplo, un datepicker para mostrar dates y otro para una notación de time:

class DateformatterManager { var formatter = NSDateFormatter() class var dateFormatManager : DateformatterManager { struct Static { static let instance : DateformatterManager = DateformatterManager() } // date shown as date in some tableviews Static.instance.formatter.dateFormat = "yyyy-MM-dd" return Static.instance } class var timeFormatManager : DateformatterManager { struct Static { static let instance : DateformatterManager = DateformatterManager() } // date shown as time in some tableviews Static.instance.formatter.dateFormat = "HH:mm" return Static.instance } // MARK: - Helpers func stringFromDate(date: NSDate) -> String { return self.formatter.stringFromDate(date) } func dateFromString(date: String) -> NSDate? { return self.formatter.dateFromString(date)! } } // Usage would be something like: DateformatterManager.dateFormatManager.dateFromString("2014-12-05") 

Otro enfoque similar sería crear solo un singleton y cambiar el formatting según la necesidad:

 class DateformatterManager { var formatter = NSDateFormatter() var dateFormatter : NSDateFormatter{ get { // date shown as date in some tableviews formatter.dateFormat = "yyyy-MM-dd" return formatter } } var timeFormatter : NSDateFormatter{ get { // date shown as time in some tableviews formatter.dateFormat = "HH:mm" return formatter } } class var shanetworkingManager : DateformatterManager { struct Static { static let instance : DateformatterManager = DateformatterManager() } return Static.instance } // MARK: - Helpers func dateStringFromDate(date: NSDate) -> String { return self.dateFormatter.stringFromDate(date) } func dateFromDateString(date: String) -> NSDate? { return self.dateFormatter.dateFromString(date)! } func timeStringFromDate(date: NSDate) -> String { return self.timeFormatter.stringFromDate(date) } func dateFromTimeString(date: String) -> NSDate? { return self.timeFormatter.dateFromString(date)! } } // Usage would be something like: var DateformatterManager.shanetworkingManager.dateFromDateString("2014-12-05") 

¿Cualquiera de esas sería una idea buena o horrible? ¿Y cambiar el formatting también es caro?

Actualización: Como Hot Licks y Lorenzo Rossi señalan, cambiar los formattings probablemente no sea una buena idea (no es seguro para subprocesss y es tan caro como volver a crear …).

Aquí tocaré con una respuesta basada en la experiencia. La respuesta es sí, el almacenamiento en caching de la aplicación NSDateFormatter es una buena idea, sin embargo, para mayor security, hay un paso que desea tomar para esto.

¿Por qué es bueno? Actuación. Resulta que crear NSDateFormatters es realmente lento. Trabajé en una aplicación altamente localizada que usaba muchos NSDateFormatters y NSNumberFormatters. Hubo momentos en los que los creamos dinámicamente codiciosamente dentro de los methods, además de tener classs que tenían su propia copy de los formateadores que necesitaban. Además, tuvimos la carga adicional de que había casos en los que también podíamos mostrar cadenas localizadas para diferentes lugares en la misma pantalla. Notamos que nuestra aplicación se estaba ejecutando lentamente en ciertos casos, y después de ejecutar Instruments, nos dimos count de que era la creación de formateador. Por ejemplo, vimos el performance golpeado cuando se desplazan vistas de tabla con una gran cantidad de celdas. Así que terminamos almacenándolos en caching creando un object singleton que selló el formateador apropiado.

Una llamada sería algo así:

 NSDateFormatter *dateFormatter = [[FormatterVender shanetworkingInstance] shortDate]; 

Tenga en count que se trata de Obj-C, pero se puede hacer el equivalente en Swift. Simplemente sucedió que nuestro estaba en Obj-C.

A partir de iOS 7, NSDateFormatters y NSNumberFormatters son "subprocesss seguros", sin embargo, como menciona Hot Licks, es probable que no quiera modificar el formatting si otro subprocess lo está utilizando. Otro +1 para almacenarlos en caching.

Y otro beneficio en el que solo pensé era en el mantenimiento del código. Especialmente si tiene un equipo grande como nosotros. Como todos los desarrolladores saben que hay un object centralizado que vende los formateadores, simplemente pueden ver si el formateador que necesitan ya existe. Si no lo hace, se agrega. Esto suele estar relacionado con la function y, por lo tanto, generalmente significa que también se necesitará un nuevo formateador en cualquier otro lado. Esto también ayuda a networkingucir los errores, porque si sucede que hay un error en el formateador, lo arregla un solo punto. Pero usualmente capturamos eso durante las testings unitarias para el nuevo formateador.

Hay un elemento más que puede agregar por security, si lo desea. Cuál es usted puede utilizar el threadDictionary de NSThread para almacenar el formateador. En otras palabras, cuando llama al singleton que venderá el formateador, esa class comtesting el threadDictionary del hilo actual para ver si ese formateador existe o no. Si existe, simplemente lo devuelve. Si no, lo crea y luego lo devuelve. Esto agrega un nivel de security, por lo que si, por alguna razón, quisiera modificar su formateador, puede hacerlo y no tener que preocuparse de que el formateador sea modificado por otro hilo.

Lo que usamos al final del día fue Singleton, que selló formateadores específicos (tanto NSDateFormatter como NSNumberFormatter), asegurando que cada hilo tuviera su propia copy de ese formateador específico (tenga en count que la aplicación se creó antes de iOS 7, lo que lo convirtió en un cosa esencial para hacer). Al hacerlo, mejoró el performance de nuestra aplicación y se deshizo de algunos efectos secundarios desagradables que experimentamos debido a la security de los hilos y los formateadores. Como tenemos la parte threadDictionary en su lugar, nunca la probé para ver si teníamos algún problema en iOS7 + sin él (es decir, se habían vuelto realmente seguros para subprocesss). Por eso agregué el "si quieres" arriba.

En mi opinión, Caching NSDateFormatter es una buena idea si su aplicación lo utiliza ampliamente o en toda su aplicación, boostá el performance de su aplicación. Si necesita eso en 1 o 2 lugares, no será una buena idea. Sin embargo, cambiar el formatting de date no es una buena idea, puede llevarlo a situaciones no deseadas. (Debe seguir el formatting actual cada vez antes de usarlo)

En una de mis aplicaciones, utilicé un singleton con tres objects de formatting de date (los tres contiene tres formattings diferentes) como properties. Y getters personalizados para cada NSDateFormatter

 + (instancetype)defaultDateManager { static DateManager *dateManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dateManager = [[DateManager alloc] init]; }); return dateManager; } // Custom Getter for short date - (NSDateFormatter *)shortDate { if (!_shortDateFormatter) { _shortDateFormatter = [[NSDateFormatter alloc] init]; [_shortDateFormatter setDateFormat:@"yyyy-MM-dd"] } return _shortDateFormatter } 

Así implementé getters personalizados para los otros dos también.

¿Por qué implementé getters personalizados? ¿Por qué no NSDateFormatter el NSDateFormatter durante la initialization de Singleton?

Es porque no quiero asignarlos en el comienzo mismo. Necesito asignarlo cuando sea necesario por primera vez (a pedido). En mi aplicación, los tres NSDateFormatters no son ampliamente utilizados, por eso elegí un patrón para implementarlo. (Mi aplicación está en el Objetivo C, por eso utilicé el código Objective C aquí)

En Objc:

Teniendo en count que NSDateFormatter se considera thread-unsafe usar un solo formateador en su aplicación puede no ser la mejor idea. Tener un formateador por hilo puede ser un path a seguir. Alternativamente, podría considerar envolver el formateador en una class segura para subprocesss.

Desde Apple Docs: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

En Swift:

Si swift ofrece security de subprocesss de la class de lo que no debería haber ningún problema para tener una instancia.

En lugar de utilizar un singleton, use la dependency injection. Recuerde seguir la regla 0, 1, infinita.

http://en.wikipedia.org/wiki/Zero_one_infinity_rule

Aquí, claramente no podemos tener 0, y mientras que 1 suena bien, si vas a usarlo de varios hilos, entonces no puedes tener uno sin que cuelgue. Entonces, infinito.

Una buena manera de enfocar esto es tener cuidado con cuántos engendras, mantener solo uno por cada hilo que abres y asegurarte de que se limpien tan pronto como hayas terminado de usarlos.

Para ayudarlo, eche un vistazo a este otro enlace stackoverflow. Me temo que mi respuesta estará en línea con la suya (mantenga el número de NSDateformatters en un mínimo). Sin embargo, pueden tener algún razonamiento que no estaba cubierto en este hilo (¡sin juego de palabras!)

¿Cómo minimizar los costos de asignación e initialization de un NSDateFormatter?

Además, si puedo preguntar: si se encuentra con esta pregunta, ¿hay algún lugar en su progtwig donde se pueda mejorar un flujo para evitar incluso la necesidad de tantos formateadores?

¿Cualquiera de esas sería una idea buena o horrible?

Presentar singletons (en cualquier momento que necesite una nueva variante de formateador) no es una buena solución. Crear formateadores no es tan caro.

En su lugar, cree forms de reutilizar y compartir instancias de formateadores y pasárselas en su progtwig, como variables regulares. Este enfoque es bastante simple de introducir en un progtwig existente.

Los instrumentos lo ayudarán a identificar dónde crea muchos formateadores en su progtwig, y ​​puede considerar cómo reutilizar los formateadores en function de esos datos.

¿Y cambiar el formatting también es caro?

No se moleste en cambiar los formateadores que comparte, a less que se utilicen en contexts locales o muy específicos (como una colección particular de vistas). Esto será mucho más fácil para lograr los resultados esperados. En su lugar, copie y luego mutate si necesita una variante de formateador compartido.

Ejemplo Swift

Basado en la respuesta de @Mobile Ben: este es un ejemplo de simple Singleton Swift.

 class YourDateFormatter { // MARK: - Properties static let shanetworkingFormatter = YourDateFormatter() /// only date format private let dateFormatter: NSDateFormatter /// only time format private let timeFormatter: NSDateFormatter // MARK: - private init private init() { // init the formatters dateFormatter = NSDateFormatter() timeFormatter = NSDateFormatter() // config the format dateFormatter.dateStyle = .MediumStyle dateFormatter.timeStyle = .NoStyle dateFormatter.doesRelativeDateFormatting = true timeFormatter.dateStyle = .NoStyle timeFormatter.timeStyle = .MediumStyle } // MARK: - Public func dateFormat(date: NSDate) -> String { return dateFormatter.stringFromDate(date) } func timeFormat(date: NSDate) -> String { return timeFormatter.stringFromDate(date) } func dateTimeFormat(date: NSDate) -> String { let dateFormat = self.dateFormat(date) let timeFormat = self.timeFormat(date) return dateFormat + " - " + timeFormat } } 

También me gustaría ampliar la respuesta de Mobile Ben proporcionando un ejemplo:

 import Foundation public class DateFormatter : NSDateFormatter{ public class func shanetworkingFormatter() -> NSDateFormatter { // current thread's hash let threadHash = NSThread.currentThread().hash // check if a date formatter has already been created for this thread if let existingFormatter = NSThread.currentThread().threadDictionary[threadHash] as? NSDateFormatter{ // a date formatter has already been created, return that return existingFormatter }else{ // otherwise, create a new date formatter let dateFormatter = NSDateFormatter() // and store it in the threadDictionary (so that we can access it later on in the current thread) NSThread.currentThread().threadDictionary[threadHash] = dateFormatter return dateFormatter } } } 

Esto se utiliza en una biblioteca, por eso puede ver el modificador público en todo momento.