Durante mucho tiempo pensé que entendía los value types.
No hace mucho, en una entrevista técnica, recibí un feedback que me hizo pensar. No fue especialmente complejo, sino sobre conceptos que uso a diario.
Me di cuenta de que, aunque los aplico constantemente, no siempre los tengo tan interiorizados como para explicarlos con la claridad y profundidad que me gustaría.
Y es que en ocasiones damos por entendidos ciertos fundamentos porque forman parte de nuestro día a día. Pero usar algo no es lo mismo que entenderlo. Y explicar un concepto con precisión es, probablemente, la mejor prueba de que lo has integrado de verdad.
No se trata de memorizar definiciones, sino de comprensión real.
Así que he decidido tomar ese feedback como una oportunidad. Una invitación a volver a las bases. Y, de paso, a actualizar el blog con reflexiones que también me ayuden a ordenar mis propias ideas.
Este artículo nace de ahí.
El modelo simplificado que todos tenemos
Durante mucho tiempo mi modelo mental fue este:
struct→ se copia.class→ se comparte referencia.- Value types → más seguros.
- Reference types → cuidado con efectos secundarios.
Y aunque eso no es incorrecto, es incompleto.
Si Swift copiara realmente cada Array cada vez que lo asignamos a otra variable, el rendimiento sería desastroso. Pasar colecciones entre capas sería carísimo. Mutar estructuras grandes sería prohibitivo.
Entonces… ¿qué está pasando realmente?
Swift no copia inmediatamente
Copy-on-Write (COW) es una estrategia de optimización que permite que los tipos con value semantics mantengan buen rendimiento.
La idea es elegante: Swift comparte el almacenamiento interno mientras nadie lo modifique. Solo crea una copia real cuando una de las instancias necesita mutar.
La copia real solo ocurre en el momento en que una de las dos variables intenta modificar el contenido. En ese instante, Swift comprueba si el almacenamiento está compartido y, si lo está, crea una copia independiente antes de aplicar la mutación.
Swift comprueba si el almacenamiento subyacente está compartido por más de una instancia. Si es único, muta directamente. Si no lo es, crea una copia antes de mutar. Sería algo así:
if isKnownUniquelyReferenced(&storage) {
// puedo mutar en sitio
} else {
// creo copia antes de mutar
}
Eso es el write del Copy-on-Write.
Swift consigue value semantics con coste amortizado similar al de compartir referencias, hasta que la mutación lo requiere.
Un pequeño ejemplo
Imagina que tienes un array a y lo asignas a otra variable b.
Aunque parezca que b es una copia, internamente ambos comparten el mismo almacenamiento. No hay copia todavía.
Este es el codigo detrás:
@State private var a: [Int] = Array(0..<5)
@State private var b: [Int] = []
Button("1) Copiar a en b (sin mutar)") {
b = a
}
Button("2) Mutar b (dispara copia)") {
b.append(999)
}
Y así podemos comprobarlo si observamos las direcciones de memoria en la imagen, antes comparten la misma dirección en memoria ...212c930, pero en el momento en que se intenta modificar b, se le asigna una nueva dirección que ya no es la misma: ...216c2f0 (b) vs ...212c930 (a)
¿Por qué esto importa realmente?
Porque cambia nuestro modelo mental.
Entender Copy-on-Write no es solo entender una optimización interna. Es entender cómo el diseño del lenguaje influye en cómo diseñamos nuestras aplicaciones.
Por ejemplo, en cómo pensamos el coste de pasar datos entre capas.
Cuando pasamos un Array entre capas, no estamos pagando el coste de una copia inmediata del almacenamiento subyacente. El coste aparece en el momento de la mutación.
Eso significa que diseñar APIs con value types no es “caro por defecto”.
El problema no es copiar; el problema es mutar sin entender el coste.
Es importante recordar que no todos los struct usan Copy-on-Write.
COW es una estrategia implementada manualmente en tipos como Array, Dictionary y String.
No es una propiedad automática de todos los value types.
Usar el feedback como catalizador
No me preparé tan a fondo como podía haberlo hecho. El feedback que recibí sirvió como catalizador: me obligó a revisar conceptos, profundizar y, finalmente, plasmar lo aprendido en este artículo. Volver a estos fundamentos no es retroceder. Es fortalecer la base sobre la que construimos todo lo demás.
No nace de la certeza absoluta, sino del proceso de entender lo que ya usamos a diario.
La próxima vez que me pregunten por Copy-on-Write quiero poder explicarlo con claridad y quizá este artículo me acerque más a ello.