let y var en Swift: no solo azúcar sintáctico, sino una clave para el rendimiento
\n\nSi alguna vez te has preguntado "¿Cuál es la diferencia entre let y var en Swift?", no estás solo. En Stack Overflow, esta pregunta ha acumulado miles de visitas, y no es casualidad. A primera vista, la respuesta parece obvia: let es una constante, var es una variable. Pero el diablo, como siempre, está en los detalles. En este artículo no solo repetiremos lo básico, sino que miraremos bajo el capó del compilador de Swift y descubriremos cómo la elección entre let y var afecta el rendimiento de tu aplicación. ¿Listos? ¡Allá vamos!
Fundamentos: ¿qué dicen los libros de texto?
\n\nEmpecemos con lo fundamental para tener una base común. En Swift:
\n\n- \n
let— declara una constante. El valor se asigna una vez y no puede modificarse después. \n var— declara una variable. El valor se puede cambiar tantas veces como se desee. \n
Ejemplo de código real:
\n\nlet maximumLoginAttempts = 3 // Constante: el número de intentos es fijo\nvar currentLoginAttempt = 0 // Variable: se incrementará\n\nParece sencillo. Pero si la diferencia fuera solo esta, la pregunta en Stack Overflow no habría atraído tanta atención. La verdadera magia comienza cuando hablamos de optimización.
\n\nRendimiento: por qué let suele ser más rápido que var
\n\nMuchos desarrolladores piensan que let y var son solo una cuestión de estilo o seguridad. En realidad, la elección puede afectar la velocidad de ejecución del programa. Así es como funciona a nivel del compilador.
1. Optimización por el compilador (SIL y LLVM)
\n\nSwift utiliza un sistema de optimización de múltiples niveles. Primero, el código se transforma en SIL (Swift Intermediate Language) y luego en LLVM IR. En cada etapa, el compilador toma decisiones basándose en si el valor es let o var.
Cuando declaras let, le das al compilador una garantía sólida: "Este valor nunca cambiará". El compilador usa esta información para:
- \n
- Eliminar comprobaciones redundantes: Si el valor de la constante se usa en un bucle, el compilador puede calcularlo una vez y almacenarlo en caché, en lugar de recalcularlo en cada iteración. \n
- Incrustación en línea (inline): Las constantes a menudo se integran directamente en el código, eliminando la necesidad de acceder a la memoria. \n
- Mejorar la gestión de memoria: Para
let, el compilador puede usar estrategias de asignación de datos más eficientes, ya que sabe que no se necesitará escribir en esa área de memoria. \n
Con var es más complicado. El compilador no puede estar seguro de que el valor no cambiará (incluso si no planeas cambiarlo). Por lo tanto, se ve obligado a generar código más conservador:
- \n
- El valor siempre se almacena en memoria (no solo en los registros del procesador). \n
- Se generan instrucciones adicionales para una posible escritura. \n
- Se bloquean algunas optimizaciones relacionadas con la reordenación de instrucciones. \n
Conclusión: let le da al compilador más libertad para optimizar. Esto es especialmente notable en secciones críticas del código: bucles, funciones recursivas, procesamiento de grandes arreglos.
2. Semántica y hilos (Thread Safety)
\n\nOtro aspecto del rendimiento es el trabajo en entornos multiproceso. let hace que el código sea inherentemente seguro para hilos. Una constante no se puede modificar, por lo que no hay riesgo de condiciones de carrera (race condition).
Con var es diferente. Si una variable se usa en varios hilos, tendrás que agregar bloqueos (NSLock, DispatchQueue) o usar operaciones atómicas. Esto inevitablemente reduce el rendimiento.
Compara los dos enfo