De acuerdo con esta publicación de blog y la respuesta actualmente más votada a esta pregunta de desbordamiento de pila , que a su vez cita la documentación de Apple , la mejor manera de crear un singleton en Swift moderno es:
class Singleton { static let sharedInstance = Singleton() } Aunque no se mencionó, probablemente también se requiera un private init() .
Para mí, una alternativa más simple sería convertir todas las propiedades y métodos a static y eliminar la propiedad sharedInstance .
Por ejemplo, supongamos que escribí una clase con una propiedad y un método, siguiendo el consejo anterior, de la siguiente manera:
class Singleton { static let sharedInstance = Singleton("whatever") var myProperty: String func myMethod() { // ... } private init(_ myProperty) { self.myProperty = myProperty } } Si el usuario necesita acceder a la propiedad en cuestión, escribiría Singleton.sharedInstance.myProperty , y si necesita llamar al método, escribiría Singleton.sharedInstance.myMethod() .
Propongo reescribir la clase de la siguiente manera:
class Singleton { static var myProperty: String = "whatever" static func myMethod() { // ... } } Entonces: menos código repetitivo y menos caracteres para escribir al acceder a la propiedad (solo Singleton.myProperty ) y al método ( Singleton.myMethod() ).
Una desventaja es que los accesos a la propiedad y el método desde dentro de la clase tendrían que estar completamente explicados ( Singleton.myProperty y Singleton.myMethod() ), en comparación con solo myProperty y myMethod() para la solución anterior.
Por lo tanto, es un poco más fácil para el usuario (quitando la parte sharedInstance ) y un poco más difícil para el escritor de la clase (que necesita agregar Singleton. delante de todos los accesos). Parece razonable que, ante una elección de diseño que favorece al usuario o al escritor de la clase, la mejor opción es favorecer al usuario.
Nadie más parece defender el método que propuse para hacer un singleton, así que tengo la sensación de que debe haber algo mal en él. ¿Alguien sería tan amable de decirme qué es?
Para mí, una alternativa más simple sería convertir todas las propiedades y métodos en estáticos y eliminar la propiedad sharedInstance.
Estos no hacen lo mismo. El enfoque recomendado no es en realidad un singleton en absoluto. Es solo un caso bien conocido. El concepto del patrón Singleton es que solo debe haber una instancia. El concierto del patrón de instancias compartidas es que puede haber más de una instancia, pero hay una que probablemente desee y le gustaría acceder fácilmente a ella.
La ventaja de las instancias compartidas es que no son mágicas. Son solo instancias. Eso significa que se pueden entregar como valores. Se pueden reemplazar con otras instancias que se pueden configurar de manera diferente. Son más fáciles de probar (porque se pueden pasar a funciones).
Los singletons verdaderos son un patrón muy rígido que solo debe usarse cuando es absolutamente necesario que no exista ninguna otra instancia, generalmente porque interactúan con algún recurso único externo de una manera que crearía conflictos si hubiera múltiples (esto es bastante raro). Incluso en este caso, en Swift, por lo general, debe hacer que init sea privado para evitar que se creen instancias adicionales.
Si mira alrededor de Cocoa, encontrará que las instancias compartidas son extremadamente comunes para cosas que serían Singletons en otros marcos, y esto ha sido muy poderoso. Por ejemplo, hay un Centro de NotificationCenter bien conocido llamado default y probablemente sea el único que hayas usado. Pero es completamente válido crear un NotificationCenter privado que sea independiente (de hecho, lo he hecho en el código de producción).
El hecho de que UIDevice.current sea la forma de acceder al dispositivo, en lugar de métodos estáticos, deja abierta la posibilidad de nuevas API que puedan manejar varios dispositivos (también ayuda con las pruebas unitarias). En las primeras versiones de iOS, el único UIScreen era .main y podría haber tenido sentido convertirlo en un singleton. Pero debido a que Apple no lo hizo, cuando se agregó la duplicación en 4.3, fue sencillo hablar de la segunda pantalla ( UIScreen.mirrored ). En general, debe ser muy lento para asumir que solo puede haber uno de algo.