Structs vs clases: valor y referencia
La decisión de diseño más importante de Swift. Semántica de valor vs referencia, cuándo usar cada una, y por qué Swift prefiere structs.
Structs y clases se parecen —ambas agrupan datos y comportamiento— pero se comportan de forma radicalmente distinta al copiarlas. Entender esa diferencia es lo que separa a un principiante de alguien que escribe Swift idiomático.
- Definir structs y clases.
- La diferencia clave: valor vs referencia.
- Métodos
mutatinge inicializadores. - Cuándo usar cada una (y por qué Swift prefiere structs).
Lo que comparten
Ambas agrupan propiedades y métodos:
struct Punto { var x: Double var y: Double func distanciaAlOrigen() -> Double { (x * x + y * y).squareRoot() }}let p = Punto(x: 3, y: 4) // init por miembros, automático en structsprint(p.distanciaAlOrigen()) // 5.0Las structs traen un inicializador por miembros gratis. Las clases no: tienes que escribirlo.
La diferencia clave: valor vs referencia
Aquí está todo. Una struct es un tipo de valor: al asignarla o pasarla, se copia. Una clase es un tipo de referencia: se comparte la misma instancia.
struct PuntoV { var x: Int }class PuntoR { var x: Int; init(x: Int) { self.x = x } }var a = PuntoV(x: 1)var b = a // COPIAb.x = 99print(a.x) // 1 ← 'a' no cambióvar c = PuntoR(x: 1)var d = c // MISMA instanciad.x = 99print(c.x) // 99 ← 'c' sí cambió, es la mismaPor qué esto lo cambia todo
Con valor (structs), cada quien tiene su copia: nadie modifica tus datos a tus espaldas. Esto elimina una categoría entera de bugs — los efectos secundarios inesperados y las condiciones de carrera. Por eso SwiftUI usa structs para las vistas y el estado: predecibilidad. Con referencia (clases), compartes una instancia, lo cual es útil cuando de verdad quieres que algo sea único y compartido (un gestor, una conexión). La regla mental: valor = datos; referencia = identidad.
Métodos mutating
Como una struct es de valor, un método que cambie sus propiedades debe marcarse mutating:
struct Contador { var valor = 0 mutating func incrementar() { valor += 1 }}var c = Contador()c.incrementar() // solo funciona si 'c' es varLas clases no lo necesitan (siempre pueden mutar sus propiedades).
Inicializadores
Las structs generan el init por miembros. Las clases lo requieren explícito:
class Usuario { let nombre: String var activo: Bool init(nombre: String, activo: Bool = true) { self.nombre = nombre self.activo = activo }}Identidad: ===
Como las clases se comparten, puedes preguntar si dos variables apuntan a la misma instancia con ===:
let x = Usuario(nombre: "Ana")let y = xprint(x === y) // true: la misma instanciaLas structs no tienen identidad (cada copia es independiente), así que === no aplica.
¿Cuál uso?
Struct (por defecto)
Datos: un modelo, una configuración, coordenadas, una respuesta de API. Swift y Apple recomiendan structs salvo que necesites lo contrario.
Clase (cuando la necesitas)
Identidad compartida o herencia: un gestor único, un objeto que muchas partes deben ver cambiar, interoperar con APIs de Objective-C.
La regla de Apple: empieza con struct
La guía oficial de Swift es clara: usa structs por defecto y pasa a clase solo cuando tengas una razón concreta (identidad compartida, herencia de clase, o requisitos de un framework). La mayoría de tus tipos deberían ser structs.
Siente la diferencia
- Crea una
structcon una propiedad, cópiala a otra variable y cambia la copia. Comprueba que el original no cambia. - Haz lo mismo con una
classy observa que sí cambia. - Añade un método
mutatinga tu struct. - Escribe un
initpersonalizado para una clase con un parámetro por defecto. - Comprueba identidad con
===en dos variables de la misma instancia.