Introducción: el eterno debate entre static y class en Swift
\nSi alguna vez has programado en Swift, seguramente te has encontrado con esta situación: necesitas crear un método o una variable que pertenezca al propio tipo, no a su instancia. Y aquí surge el dilema: ¿usar la palabra clave static o class? En Stack Overflow, esta pregunta ha acumulado miles de visitas, y no es para menos: la diferencia es sutil, pero críticamente importante para diseñar correctamente la arquitectura de una aplicación.
En este tutorial, aclararemos de una vez por todas en qué se diferencian static y class en Swift, cuándo usar cada uno y cómo no caer en una trampa con la herencia. ¡Allá vamos!
¿Qué son las propiedades y métodos de tipo?
\nAntes de profundizar, recordemos los conceptos básicos. En Swift, cada clase, estructura o enumeración tiene dos tipos de miembros:
\n- \n
- De instancia (instance) — pertenecen a un objeto concreto. Para usarlos, necesitas crear una instancia:
let obj = MiClase(). \n - De tipo (type) — pertenecen al propio tipo. Se invocan a través del nombre del tipo:
MiClase.algunMetodo(). \n
Para crear propiedades y métodos de tipo en Swift se usan las palabras clave static y class. Y aquí es donde empieza lo interesante.
Static: estricto e inmutable
\nLa palabra clave static es tu amiga confiable cuando quieres crear una propiedad o método que no se pueda redefinir en una subclase. Funciona igual para clases, estructuras y enumeraciones.
Veamos un ejemplo. Imagina que estamos creando una aplicación para registrar estudiantes y queremos almacenar el número total de objetos creados:
\n\nclass Estudiante {\n static var totalCount = 0\n \n let nombre: String\n \n init(nombre: String) {\n self.nombre = nombre\n Estudiante.totalCount += 1\n }\n \n static func imprimirTotal() {\n print("Total de estudiantes: \\(totalCount)")\n }\n}\n\nlet e1 = Estudiante(nombre: "Iván")\nlet e2 = Estudiante(nombre: "María")\nEstudiante.imprimirTotal() // Imprime: Total de estudiantes: 2\n\nObserva que accedemos a totalCount e imprimirTotal() a través del nombre de la clase Estudiante, no mediante una instancia. Esto es útil para contadores, configuraciones predeterminadas o almacenamiento en caché de datos.
Static en estructuras y enumeraciones
\nEn estructuras y enumeraciones, static es la única forma de declarar miembros de tipo. Por ejemplo:
enum MetodoHTTP {\n case get\n case post\n case put\n \n static let metodoPorDefecto: MetodoHTTP = .get\n}\n\nstruct ConfigApp {\n static let apiURL = "https://api.example.com"\n static let tiempoLimite: Double = 30.0\n}\n\nAquí, static funciona perfectamente porque las estructuras y enumeraciones no admiten herencia, por lo que no es necesaria la redefinición.
Class: flexibilidad de herencia
\nLa palabra clave class es una herramienta para quienes trabajan con clases (y solo con ellas) y quieren permitir que las subclases redefinan métodos o propiedades. class no se puede usar en estructuras ni enumeraciones.
Supongamos que tenemos una clase base Vehiculo y queremos que cada subclase pueda proporcionar su propia versión de la descripción:
class Vehiculo {\n class func descripcion() -> String {\n return "Esto es un vehículo"\n }\n}\n\nclass Coche: Vehiculo {\n override class func descripcion() -> String {\n return "Esto es un automóvil"\n }\n}\n\nclass Bicicleta: Vehiculo {\n override class func descripcion() -> String {\n return "Esto es una bicicleta"\n }\n}\n\nprint(Vehiculo.descripcion()) // Esto es un vehículo\nprint(Coche.descripcion()) // Esto es un automóvil\nprint(Bicicleta.descripcion()) // Esto es una bicicleta\n