Swift de Cero a Experto #1: Tipos de datos, operadores y cómo Swift piensa en memoria
Primer artículo de la serie Swift de Cero a Experto. Exploramos los tipos de datos fundamentales, operadores y por qué Swift decide poner las cosas en el stack.
¿Cuántos años llevas escribiendo Swift? ¿Dos, cinco, ocho? Ahora déjame hacerte otra pregunta: ¿sabes exactamente qué pasa cuando escribes let name = "Juan"? No me refiero a “se crea una constante”. Me refiero a dónde vive esa constante en memoria, por qué el compilador elige ponerla ahí, y qué implicaciones tiene esa decisión para el rendimiento de tu app.
Si no puedes responder con confianza, no te preocupes. La mayoría de los desarrolladores iOS — incluso seniors — usan Swift sin entender realmente cómo piensa el lenguaje por debajo. Y eso no es un problema… hasta que lo es. Hasta que tu app se traba, consume memoria sin control, o tienes un memory leak que no logras rastrear.
Usar Swift sin entender su modelo de memoria es como conducir un auto sin saber que tiene espejos retrovisores. Funciona, hasta que necesitas cambiar de carril.
Bienvenido a Swift de Cero a Experto
Este es el primer artículo de una serie de 20 donde vamos a recorrer todo el lenguaje Swift — desde los tipos de datos más básicos hasta la programación funcional avanzada. Pero no va a ser un recorrido superficial. En cada artículo vamos a tejer un hilo conductor: cómo Swift maneja la memoria y cómo el compilador se beneficia del diseño del lenguaje.
¿Por qué? Porque entender esto te transforma de alguien que usa Swift a alguien que domina Swift. Y la diferencia entre esos dos perfiles se nota en las entrevistas, en el código que produces, y en los bugs que nunca llegas a crear.
- Fase 1: Fundamentos — tipos, colecciones, strings, control de flujo, funciones
- Fase 2: Swift OO — closures, enums, structs vs classes, herencia, init/deinit
- Fase 3: Avanzado — opcionales, error handling, protocols, generics, macros
- Fase 4: Experto — ARC, memory safety, access control, operadores avanzados, programación funcional
Hoy arrancamos con los cimientos: constantes, variables, tipos de datos y operadores. Y al final, vamos a aterrizar todo en memoria — porque incluso algo tan simple como un Int tiene una historia que contar.
Constantes y variables: let vs var
En Swift, todo empieza con una declaración. Tienes dos opciones:
let pi = 3.14159 // Constante — no puede cambiarvar counter = 0 // Variable — puede cambiarcounter += 1 // ✅ Esto funciona// pi = 3.14 // ❌ Error de compilaciónlet declara una constante. var declara una variable. Simple. Pero hay algo más profundo aquí que vale la pena entender.

Cuando usas let, no solo estás comunicando tu intención al equipo — estás dándole información al compilador. Le estás diciendo: “este valor no va a cambiar jamás”. Y el compilador usa esa promesa para tomar decisiones de optimización. Puede almacenar ese valor directamente en un registro del procesador, puede eliminar comprobaciones innecesarias, puede incluso sustituir las referencias al valor por el valor mismo (constant folding).
Type annotations vs type inference
Swift es un lenguaje fuertemente tipado — cada valor tiene un tipo, sin excepciones. Pero no siempre necesitas escribirlo explícitamente:
// Type annotation — tú declaras el tipolet age: Int = 30let name: String = "Swift"
// Type inference — el compilador deduce el tipolet age = 30 // El compilador infiere Intlet name = "Swift" // El compilador infiere StringAmbas formas producen exactamente el mismo resultado. La inferencia de tipos no es magia ni tiene costo en rendimiento — sucede completamente en tiempo de compilación. Para cuando tu código se ejecuta en el dispositivo, el compilador ya sabe el tipo exacto de cada variable. No hay ambigüedad, no hay decisiones en runtime.
Los tipos de datos fundamentales
Swift tiene un conjunto de tipos primitivos que forman la base de todo lo demás. Vamos a conocerlos uno por uno — y a entender qué son realmente.
Enteros: Int
let users = 1_000_000 // Los guiones bajos mejoran legibilidadlet temperature = -15 // Los enteros pueden ser negativosInt en Swift se adapta a la plataforma: en un dispositivo de 64 bits (todos los iPhones desde el 5s), Int es un entero de 64 bits. Eso significa que puede almacenar valores desde -9,223,372,036,854,775,808 hasta 9,223,372,036,854,775,807.
Swift también ofrece variantes con tamaño explícito: Int8, Int16, Int32, Int64, y sus versiones sin signo: UInt8, UInt16, UInt32, UInt64.
let byte: UInt8 = 255 // Máximo valor para 8 bits sin signolet small: Int8 = 127 // Máximo valor para 8 bits con signo// let overflow: UInt8 = 256 // ❌ Error de compilación — fuera de rangoEse último ejemplo es importante. En C o C++, un overflow como ese compilaría silenciosamente y te daría un resultado inesperado. En Swift, el compilador lo atrapa antes de que tu código se ejecute. Esto no es un detalle menor — es una filosofía de diseño.
Int8/UInt8→ 1 byte (8 bits)Int16/UInt16→ 2 bytes (16 bits)Int32/UInt32→ 4 bytes (32 bits)Int64/UInt64→ 8 bytes (64 bits)Int/UInt→ 8 bytes en plataformas de 64 bits
Decimales: Double y Float
let pi = 3.14159 // El compilador infiere Doublelet gravity: Float = 9.8 // Explícitamente FloatDouble tiene precisión de 64 bits (al menos 15 dígitos decimales). Float tiene precisión de 32 bits (6 dígitos). Swift infiere Double por defecto cuando escribes un número con punto decimal — y esa es la opción correcta en la mayoría de los casos. No uses Float a menos que tengas una razón específica (como interoperar con APIs gráficas que esperan Float).
Booleanos: Bool
let isActive = truelet hasPermission = falseUn Bool ocupa 1 byte en memoria, aunque técnicamente solo necesita 1 bit. ¿Por qué? Porque la unidad mínima que el procesador puede direccionar es un byte. Es un trade-off entre eficiencia de memoria y eficiencia de acceso.
Strings (introducción)
let greeting = "Hola, Swift"let interpolated = "I'm \(age) years old"let multiline = """ This is a string that spans multiple lines and respects indentation """Los strings merecen su propio artículo — y lo tendrán en el #3 de esta serie. Por ahora, lo que necesitas saber es que String es un tipo de valor en Swift (es un struct), pero su contenido real — los caracteres — se almacena en un buffer que puede vivir en el heap. Volveremos a esto.
Type Safety: el compilador como tu aliado
Swift no te deja mezclar tipos sin ser explícito:
let integer = 42let decimal = 3.14// let sum = integer + decimal // ❌ Error: no puedes sumar Int + Double
let sum = Double(integer) + decimal // ✅ Conversión explícita¿Esto es molesto? A veces. ¿Es intencional? Absolutamente. Swift prefiere que seas explícito sobre las conversiones porque las conversiones implícitas son una fuente constante de bugs sutiles en otros lenguajes.
Piensa en JavaScript, donde "5" + 3 da "53" (concatenación) pero "5" - 3 da 2 (aritmética). Eso nunca pasa en Swift. El compilador te obliga a decir exactamente qué quieres.

Operadores
Swift incluye los operadores que esperas de cualquier lenguaje moderno, pero con algunos detalles que vale la pena conocer.
Operadores aritméticos
let sum = 10 + 3 // 13let difference = 10 - 3 // 7let product = 10 * 3 // 30let division = 10 / 3 // 3 (división entera)let remainder = 10 % 3 // 1
let realDivision = 10.0 / 3.0 // 3.333... (división decimal)Un detalle clave: la división entre enteros descarta el decimal. 10 / 3 es 3, no 3.333. Esto no es un bug — es el comportamiento correcto para la aritmética de enteros. Si quieres el resultado decimal, al menos uno de los operandos debe ser Double.
Otro detalle importante: los operadores aritméticos de Swift detectan overflow por defecto. Si intentas sumar dos Int8 que exceden su rango, tu app crashea en debug con un mensaje claro. Esto es intencional — es mejor un crash predecible que un resultado silenciosamente incorrecto.
// Si necesitas overflow wrapping (como en C), usa los operadores especiales:let wrapped = Int8.max &+ 1 // -128 (overflow wrapping)Operadores de comparación y lógicos
// Comparación1 == 1 // true2 != 1 // true2 > 1 // true1 < 2 // true1 >= 1 // true2 <= 1 // false
// Lógicoslet a = truelet b = false!a // false (NOT)a && b // false (AND)a || b // true (OR)Los operadores lógicos && y || usan evaluación en cortocircuito (short-circuit evaluation). Esto significa que a && b no evalúa b si a es false — porque el resultado ya es false sin importar qué sea b. Esto no es solo una optimización: es algo que puedes usar intencionalmente:
// El segundo check solo se ejecuta si el primero es trueif user != nil && user!.isActive { // Seguro — nunca llegamos al force unwrap si user es nil}El operador ternario
let access = age >= 18 ? "Allowed" : "Denied"Compacto y útil, pero no abuses. Si la condición o los resultados son complejos, un if/else es más legible.
Nil-Coalescing: ??
let name: String? = nillet greeting = "Hello, \(name ?? "visitor")"// "Hello, visitor"Este operador merece un preview porque lo vas a usar constantemente. Dice: “usa el valor si existe, si no, usa este valor por defecto”. Profundizaremos en opcionales en el artículo #11, pero vale la pena que lo veas desde ahora.
Operadores de rango
// Rango cerrado — incluye ambos extremosfor i in 1...5 { print(i) // 1, 2, 3, 4, 5}
// Rango semi-abierto — excluye el límite superiorfor i in 0..<5 { print(i) // 0, 1, 2, 3, 4}
// Rango parciallet names = ["Ana", "Bob", "Carlos", "Diana"]let firstTwo = names[..<2] // ["Ana", "Bob"]let fromThird = names[2...] // ["Carlos", "Diana"]El rango semi-abierto (0..<array.count) es particularmente útil para iterar arrays — y es tan común que Swift ofrece alternativas aún más expresivas que veremos en artículos futuros.
Tuplas: agrupación ligera
Las tuplas permiten agrupar múltiples valores en uno solo — sin necesidad de crear un struct o una clase:
let coordinate = (latitude: 19.4326, longitude: -99.1332)print(coordinate.latitude) // 19.4326
// Descomposiciónlet (lat, lon) = coordinateprint(lat) // 19.4326
// Ignorar valores con _let httpResponse = (200, "OK")let (statusCode, _) = httpResponseprint(statusCode) // 200Las tuplas son tipos de valor compuestos. Su tamaño en memoria es la suma del tamaño de sus componentes. La tupla (Int, Bool) ocupa 9 bytes: 8 del Int + 1 del Bool (más posible padding de alineación).
Type Aliases: nombres que comunican
typealias Speed = Doubletypealias Coordinate = (latitude: Double, longitude: Double)
let maxSpeed: Speed = 120.0let location: Coordinate = (19.4326, -99.1332)Un typealias no crea un tipo nuevo — es simplemente otro nombre para un tipo existente. No tiene costo en memoria ni en rendimiento. Es azúcar sintáctica para mejorar la legibilidad de tu código.
¿Dónde vive todo esto? Pensando en memoria
Llegamos a la parte que hace diferente a esta serie. Todo lo que acabamos de ver — Int, Double, Bool, tuplas, let, var — son tipos de valor. Y los tipos de valor, en la mayoría de los casos, viven en el Stack.
Si ya leíste Dominando Instruments (Parte 2), recordarás que el Stack es esa pila de memoria rápida y predecible donde cada función crea un “frame” al entrar y lo destruye al salir. No hay malloc, no hay free, no hay ARC. Solo un puntero que sube y baja.

Veamos esto con un ejemplo concreto. Navega paso a paso para ver cómo el stack crece y se destruye:
¿Cómo se mueve el Stack?
Haz clic en cada paso para ver cómo el stack frame crece y se destruye.
Todo este proceso es increíblemente rápido — mover un puntero es literalmente una instrucción del procesador.

- Enteros (
Int,Int8,Int16,Int32,Int64) - Decimales (
Double,Float) - Booleanos (
Bool) - Tuplas (la suma de sus componentes)
- Variables locales de tipos de valor
- Parámetros de funciones
¿Y cuándo las cosas se van al Heap?
Hay casos donde incluso un tipo de valor termina en el Heap:
- Cuando es capturado por un closure (lo veremos en el artículo #6)
- Cuando se almacena en un contenedor existencial (artículo #13)
- Cuando el tipo de valor es demasiado grande para el stack
- Los tipos por referencia (
class,actor) siempre van al Heap
Pero por ahora, con los tipos que hemos visto hoy, estamos firmemente en territorio del Stack. Y eso es una gran noticia para el rendimiento.
El compilador como tu copiloto
Algo que quiero que te quede de este primer artículo: el compilador de Swift no es un obstáculo — es tu mejor herramienta. Cada restricción que impone (type safety, overflow detection, conversiones explícitas) es una clase de bug que elimina antes de que tu código se ejecute.
Piensa en el costo: cero en runtime. El compilador hace todo su trabajo antes de generar el binario. Cuando tu app corre en el dispositivo del usuario, no hay verificaciones de tipo, no hay checks de overflow (en release), no hay inferencia. Todo eso ya pasó. Lo que queda es código limpio, optimizado, que sabe exactamente con qué tipos está trabajando.
El compilador de Swift no te pone obstáculos — te construye una autopista. Y cuanta más información le des (tipos explícitos, constantes con let, value types), más lisa es esa autopista.
Recapitulación
Hoy cubrimos los cimientos de Swift:
letyvar— constantes y variables, y por quéletayuda al compilador- Tipos fundamentales —
Int,Double,Float,Bool,String(intro) - Type Safety — el compilador verifica tipos en compilación, cero costo en runtime
- Type Inference — el compilador deduce tipos automáticamente
- Operadores — aritméticos (con detección de overflow), comparación, lógicos, ternario, nil-coalescing, rangos
- Tuplas — agrupación ligera de valores
- Type aliases — nombres descriptivos sin costo
- Memoria — todo lo de hoy vive en el Stack, rápido y predecible
Lo que viene
En el próximo artículo vamos a sumergirnos en las colecciones: Array, Set y Dictionary. Y ahí es donde la historia de la memoria se pone interesante, porque las colecciones son tipos de valor… pero su contenido vive en el Heap. Vamos a hablar de copy-on-write, una de las optimizaciones más elegantes de Swift, y de por qué el compilador puede eliminar copias que nunca necesitaron existir.

Nos vemos la próxima semana.
Dominar Swift no es memorizar sintaxis — es entender las decisiones de diseño que hicieron de este lenguaje lo que es. Y eso empieza aquí, desde los cimientos.
Relacionados
-
- swift
- ios
- performance
Dominando Instruments (Parte 2): Stack vs. Heap, simbolización y detección temprana
Entiende cómo tu app gestiona la memoria, por qué los dSYMs son críticos, y cómo detectar problemas de rendimiento antes de abrir Instruments.
-
- swift
- ios
- performance
Dominando Xcode Instruments: modelos mentales, cuellos de botella y Signposts
Aprende a pensar como un detective del rendimiento. Desglosamos Instruments desde los modelos mentales hasta la resolución práctica de hangs y la instrumentación con Signposts.
-
- swift
- concurrency
- ios
Guía completa de Swift 6.2: Approachable Concurrency explicado
Guía interactiva con las 5 feature flags de Approachable Concurrency en Xcode 26, configuración recomendada, y guía de migración paso a paso.