wandres.dev
EL MODELO DE TIPOS · structs, enums, protocolos

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.

⏱ 15 min

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.

🎯 Al terminar esta lección sabrás
  • Definir structs y clases.
  • La diferencia clave: valor vs referencia.
  • Métodos mutating e 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 structs
print(p.distanciaAlOrigen()) // 5.0

Las 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          // COPIA
b.x = 99
print(a.x)         // 1  ← 'a' no cambió

var c = PuntoR(x: 1)
var d = c          // MISMA instancia
d.x = 99
print(c.x)         // 99 ← 'c' sí cambió, es la misma
Por 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 var

Las 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 = x
print(x === y)   // true: la misma instancia

Las 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
  1. Crea una struct con una propiedad, cópiala a otra variable y cambia la copia. Comprueba que el original no cambia.
  2. Haz lo mismo con una class y observa que sí cambia.
  3. Añade un método mutating a tu struct.
  4. Escribe un init personalizado para una clase con un parámetro por defecto.
  5. Comprueba identidad con === en dos variables de la misma instancia.