Escrito por Reinder de Vries el 10 de enero de 2019 en Desarrollo de aplicaciones, Swift
Un singleton es una clase de la que existe exactamente una instancia, a la que se puede acceder globalmente. ¿Cómo se crea un singleton en Swift? ¿Y por qué deberías o no deberías?
En este tutorial, nos sumergiremos en singletons en Swift. Aprenderás qué es el patrón de diseño único y por qué es útil. Discutiremos la sintaxis para crear singletons en Swift. Y nos meteremos en casos de uso buenos y malos para singletons.
Listo? Vamos.
- ¿Qué Es Un Singleton?
- Codificar Un Singleton En Swift
- Cuándo Usar Singleton
- Lectura Adicional
¿Qué Es Un Singleton?
Un singleton es una clase de la que solo existe una instancia. Algunos ejemplos:
- Una empresa tiene solo un
CEO
- Una clase API tiene solo una cola de solicitud en serie
- Un sistema operativo tiene solo un sistema de archivos
- Un cuerpo del sistema solar gira alrededor de un punto gravitacional
- Una aplicación que hace E/S tiene solo un sistema predeterminado
FileManager
- Un avión tiene solo una cubierta de vuelo
El segundo atributo de un singleton es que tiene un punto de acceso global. Puede acceder a un singleton, por ejemplo. funciones de llamada en él, desde cualquier lugar del código de la aplicación.
Entonces, para resumir:
- Un singleton es una clase que tiene una sola instancia
- Se puede acceder a él de forma global, es decir, en cualquier lugar de su código
En el desarrollo práctico de iOS, usa singleton a menudo. Clases típicas como NotificationCenter
, UserDefaults
, SKPaymentQueue
y FileManager
tienen propiedades shared
o default
que son singletones.
En otras ocasiones, es posible que desee crear un singleton usted mismo. Un buen caso de uso es una clase API
que expone una instancia individual a través de su propiedad shared
. Se utiliza API.shared.makeAPICall()
para acceder a la API a través de una sola instancia unificada. Esto le permite, por ejemplo, administrar llamadas de API en serie.
Antes de discutir cuándo se usan mejor los singleton (y cuándo no), averigüemos cómo codificar un singleton en Swift.
Contrata como desarrollador de iOS
Aprende a crear aplicaciones de iOS 14 con Swift 5
Inscríbete en mi curso de desarrollo de iOS y aprende a comenzar tu carrera como desarrollador de iOS profesional.
Codificar un Singleton en Swift
Esta es la mejor manera de crear un singleton en Swift:
class API{ static let shared = API() private init() { // Set up API instance }}
Y así es como se usa el singleton:
API.shared.doSomething()
Estamos creando una clase API
que tiene una propiedad estática llamada shared
. Esta propiedad no se puede cambiar una vez establecida, porque es una constante y se declara estáticamente.
Eso significa que podemos acceder a la propiedad shared
a través de la clase API
. Esto a menudo se llama propiedad de clase. Compare esto con una propiedad de instancia normal, a la que solo se puede acceder a través de una instancia de una clase.
Lo interesante es que la propiedad shared
inicializa una instancia de API
dentro de la clase API
. Estamos creando un objeto API
al que se puede acceder a través de la clase API
. Pero hay más
El inicializador de clases init()
está marcado con private
. Esta palabra clave private
garantiza que la clase API
solo se pueda inicializar dentro de la clase API
.
En otras palabras, no puede crear una instancia de API
fuera de la clase API
. Esto garantiza que el objeto API
que hemos creado sea la única instancia de nuestro código. Después de todo, no puedes crear más.
Y ahora nos hemos asegurado de que la clase API
se ajuste a los dos atributos de un singleton:
- Gracias a la propiedad estática
shared
, se puede acceder globalmente a la instanciaAPI
- Gracias a
private init()
, la claseAPI
no se puede inicializar fuera de la claseAPI
Todo esto puede sonar un poco abstracto, así que expandamos el ejemplo anterior con un código más práctico. Esto es lo que:
La clase API
es casi la misma. Sigue siendo un singleton, y todavía usa esos bits de código static let shared = API()
y private init()
.
Esto es lo que ha cambiado:
- La clase
API
ahora tiene una propiedadisRequestPending
. Aquí es donde comienza el peligro See Vea cómo el booleanoisRequestPending
garantiza que solo se pueda realizar una solicitud de API a la vez. (Tenga en cuenta queisRequestPending
es una propiedad de instancia.) - La clase
API
también tiene una funciónmakeAPIRequest()
. Imagine que podemos usar esta función para recuperar algunos datos de una API de servicio web, como la de Twitter. En la función, puede ver que solo se puede realizar una solicitud cuando no hay ninguna otra solicitud pendiente actualmente. - La clase
API
también tiene una funciónonReturnAPIRequest()
. Esta función se invoca cuando regresa la solicitud de API, es decir, los datos en línea se han descargado en la aplicación. El booleanoisRequestPending
se establece de nuevo enfalse
y se procesan los datos de la solicitud.
Y así es como podemos usar el singleton API
en cualquier lugar de nuestro código:
API.shared.makeAPIRequest()
Hay algo más que tenemos que discutir. La clase API
ahora administra algo llamado state. Puedes ver el «estado» como un sentimiento: eres feliz o estás triste, o estás enojado, y así sucesivamente. Puede cambiar de un estado a otro.
La clase API
puede cambiar entre dos estados:
- Un estado en el que
isRequestPending
esfalse
- Un estado en el que
isRequestPending
estrue
Como aprenderás en la siguiente sección, el estado y los singletons pueden causar todo tipo de estragos en tu código. Administrar mal el estado es la mayor razón para el mal uso de singleton.
Cuándo Usar monoplazas
¿Cuándo usar monoplazas? El libro Design Patterns: Elements Of Reusable Object-Oriented Software de the Gang of Four tiene lo siguiente que decir. Utilice el patrón singleton cuando:
- debe haber exactamente una instancia de una clase, y debe ser accesible para los clientes desde un punto de acceso bien conocido
- cuando la única instancia debe ser extensible mediante subclase, y los clientes deben poder usar una instancia extendida sin modificar su código
Eso es complejo, pero a lo que se reduce es a:
- Use un singleton cuando su código no requiera más de una instancia de una clase (es decir, el CEO de la compañía)
- y cuando debe ser accesible desde cualquier parte de su código (es decir, desde cualquier lugar de su código)., el sistema de archivos)
Otro caso de uso es la subclase. Una variable global en su código no puede ser fácilmente subclasificada, por lo que es por eso que usa una clase singleton. Además, los singletes se pueden probar por unidad mediante inyección de dependencias. Reemplace la instancia API
por una instancia APIMock
y obtenga la capacidad de realizar llamadas a la API de prueba unitaria sin realizar las solicitudes de red reales.
¿Y cuándo no utiliza monoplazas? Para responder a esa pregunta, tendremos que volver al principio de estado que discutimos anteriormente.
Un escollo común para los desarrolladores de iOS principiantes es administrar mal el estado y sus dependencias. Imagine que está creando una aplicación que utiliza la clase API
con la que trabajamos anteriormente.
Cada vez que expande la clase API, agrega más y más propiedades, como:
- Una propiedad
userID
que realiza un seguimiento del usuario que ha iniciado sesión, una vez que se ha realizado la llamada a la APIlogin()
- Una propiedad
tweets
con datos de Twitter, una vez que se ha realizado la llamadagetTweets()
- Una propiedad
spinner
con unUIActivityIndicatorView
que agrega a un controlador de vista cuando se ha iniciado una solicitud
Al principio, esto tiene mucho sentido. Después de todo, se puede acceder a la clase API
en cualquier parte de su código. Por lo tanto, en el Controlador de vista de Tweet puede usar la matriz API.shared.tweets
, y en el Controlador de configuración puede usar userID
para indicar rápidamente a la API cuya configuración debe cambiar.
Desafortunadamente, su estado ahora está en todas partes. La clase API
tiene dependencias para un grupo de clases que no están relacionadas con la responsabilidad única de la clase API. Tu código se ha convertido en un plato de espaguetis, todo enredado. El código puede funcionar bien, pero es imposible de mantener y extender.
veamos un ejemplo. La función onReturnAPIRequest()
que definimos anteriormente está a punto de acoplarse estrechamente
Esto es lo que estamos considerando:
- Se llama a
onReturnAPIRequest()
cuando regresa una solicitud de servicio web de API, es decir, cuando los datos entran en la aplicación. Estos datos deben ir a alguna parte, por ejemplo, un Controlador de vista de Tweets. ¿Cómo pasa los datos deAPI
al controlador de vista? - Una opción obvia es crear una referencia a
viewController
en la claseAPI
. Cuando llegan los datos, puedes codificar algo comoviewController.tweets = tweetsData
. Esta es una arquitectura pobre, desafortunadamente, porque ahoraAPI
y el controlador de vista están estrechamente acoplados. Es imposible (o difícil) realizar pruebas unitarias, y es probable que cree problemas al extender cualquiera de las clases. - Es mejor elegir un mecanismo que no combine estrechamente ambas clases. Una opción sería pasar un cierre a
onReturnAPIRequest()
, que se ejecuta cuando regresa la solicitud. Este cierre puede contener código para manejar los datos entrantes. Otra opción sería usarNotificationCenter
para pasar los datos al controlador de vista, o usar una claseDatabase
para manejar los datos.
El patrón de diseño singleton ha ganado cierta controversia, simplemente porque es fácil de usar incorrectamente. Al usar singletons, tenga en cuenta el estado y las dependencias. El hecho de que sea fácil tener acceso global al estado, no significa que sea una buena idea.
Contrata como desarrollador de iOS
Aprende a crear aplicaciones de iOS 14 con Swift 5
Inscríbete en mi curso de desarrollo de iOS y aprende a comenzar tu carrera como desarrollador de iOS profesional.
Leer Más
Y eso es todo lo que hay! Conocer singletons es un objetivo que vale la pena, especialmente si está interesado en la arquitectura de aplicaciones y el diseño de sistemas.