Swift

Swift – Variables y Constantes

Comenzamos nuestra serie de tutoriales Swift aprendiendo sobre qué es una variables, una constante o una anotación, que es la inferencia de tipos, etc.

Comenzaremos en este punto, el más básico a mi modo de ver y en futuras publicaciones iremos avanzando hacia temáticas más avanzadas y claves en nuestro estudio.

Variables

Antes de entrar a ver algo de código, ¿qué es una variable?

Una variable es un segmento de memoria, un espacio asignado en la memoria RAM de nuestro dispositivo con un tipo de dato definido. Es decir las variables se crean en la memoria RAM pero no todos los datos son iguales, no es lo mismo tener un valor de: 1 a otro valor de: “hola”.

El primero es un dato entero, un número, mientras que el segundo es una cadena de caracteres, donde cada carácter (en dependencia de la codificación) puede ocupar un byte (8 bits), es decir que en la cadena “hola” tendríamos cuatro bytes (32 bits).

Por este motivo la asignación de memoria no es un tema trivial y depende siempre del tipo de dato con el que estemos trabajando.

Siempre que definimos una variable, ya sea de manera explicita o por inferencia, el tipo tiene que estar definido por el compilador y es lógico, sobre todo a nivel de optimización.

De esto último no ser así incurriríamos en procesos donde habría que almacenar los datos basados en su tamaño, para luego al momento de acceder a estos determinar el tipo de dato que está almacenado. Una vez que este es modificado obligatoriamente tendríamos que hacer un reajuste del segmento de memoria o asignar otro distinto si no hay espacio disponible junto a este.

Todo esto incurre en ciclos de reloj (trabajo de microprocesador) bastante grandes cuando sumamos todas las aplicaciones y servicios de un sistema operativo haciendo todas estas operaciones al mismo tiempo. Este enfoque no es el actual porque básicamente es del todo innecesario. 

Por este motivo al conocer el tipo de dato ya se asigna desde un inicio el espacio de memoria asociado al máximo que pudiera ocupar un tipo de dato.

El Tipo Int

El tipo de dato Int es uno de los tantos que tenemos disponibles en el lenguaje Swift. Int viene del Inglés Integer que a su vez significa Entero en castellano, lógicamente este tipo de dato, o la palabra clave Int, es la vía en la que el lenguaje nos permite especificar o clasificar que queremos almacenar un número entero.

Ojo, tomamos el tipo de dato Int para ejemplificar lo que estamos explicando, más adelante veremos el resto de tipos de datos en Swift.

Veamos un ejemplo:

var edadInt = 30

Las variables en Swift se declaran con la palabra clave var, y que es una contracción de la palabra en Inglés variable, y que no por casualidad significa variable en castellano. Como su propio nombre indica el contenido de estos segmentos de memoria puede ser variado, es decir que tenemos acceso de lectura pero también de escritura repetidamente, podemos variar su contenido n veces.

En el ejemplo anterior hemos declarado una variable de nombre edadInt y la igualamos al valor 30, pero como ya hemos comentado este contenido es variable y el hecho de estar almacenado en un espacio de memoria previamente creado para un tipo de dato entero (Int) tiene ya establecido unos márgenes bien definidos.

Veamos cuales son estos márgenes con los cuales podemos jugar para variar nuestro valor inicial de 30:

print("\nInt (64 bits or 32 bits by default) minimum value: \(Int.min)")
print("Int (64 bits or 32 bits by default) maximum value: \(Int.max)")

Si ejecutamos este código en Playground obtendremos la siguiente salida en pantalla (en un dispositivo de 64 bits):

Int (64 bits or 32 bits by default) minimum value: -9223372036854775808

Int (64 bits or 32 bits by default) maximum value: 9223372036854775807

Donde podemos constatar, en dependencia de si nuestro dispositivo es de 32 bits o 64 bits, los rangos del tipo de dato Int, que van desde el valor mínimo hasta el máximo mostrado en la salida, lo que sería lo mismo que decir que nuestro valor de 30 puede ser modificado dentro de esos límites negativos y positivos.

¿No es demasiado?

Para entender mejor el tema de la asignación de espacio a estas variable sigamos con el ejemplo básico anterior y hagamos un análisis:

¿Acaso no les parece un desperdicio de espacio el asignado a la variable edadInt? Teniendo en cuenta que un ser humano vive un promedio de 80 años, pues está claro que sí, más aún cuando una edad negativa jamás será posible.

Siempre que no hayan limitantes (de frameworks y otros factores), podemos usar una variante del tipo Int y que sería UInt. La U viene del inglés Unsigned y que significa “sin signo”, y sí, en efecto es una versión de Int que donde solo contamos con valores positivos, algo mucho más coherente con nuestra necesidad real. 

¿Cuándo no usar la Inferencia de Tipo?

Declaremos nuevamente la variable edad pero ahora declarémosla de tipo UInt, así:

var edadUInt: UInt = 30

Podemos observar que es muy similar a la declaración anterior, solo que esta vez no hemos usado la inferencia de datos y hemos especificado de manera explícita que nuestra variable será de tipo UInt (unsigned integer).

Le estamos diciendo al compilador que nuestra espacio de memoria almacenará un tipo de dato entero pero que no tendrá signo.

Un tipo de dato así es mucho más coherente con nuestra variable edad pero ¿acaso estamos ahorrando en espacio?

Imprimamos los límites del tipo UInt para comprobarlo, similar a la vez anterior:

print("\nUInt (64 bits or 32 bits by default) minimum value: \(UInt.min)")
print("UInt (64 bits or 32 bits by default) maximum value: \(UInt.max)")

Esto produciría (en un dispositivo de 64 bits) una salida en pantalla como la siguiente:

UInt (64 bits or 32 bits by default) minimum value: 0

UInt (64 bits or 32 bits by default) maximum value: 18446744073709551615

Claramente ya no tenemos un mínimo negativo pero el número máximo positivo es mucho mayor que el anterior (Int), y es que resulta que se ha sumado la capacidad o mejor dicho la capacidad sigue siendo la misma solo que hemos eliminado la posibilidad de valores negativos. Por eso es que sigue siendo un espacio de memoria de 64 bits o de 32 bits en dependencia de nuestro dispositivo.

Optimizando Nuestra Variable

¿Podemos ir más allá? Ya tenemos valores positivo, pero con segmentos de memoria de 64 bits (o 32 bits), ¿acaso no existen otros más pequeños? Pues sí, contamos en general con:

Con Signo (- / +)Sin Signo (+)
Int64 (Este tipo de dato solo existe en dispositivos 64 bits)
UInt64 (Este tipo de dato solo existe en dispositivos 64 bits)
Int32UInt32
Int16UInt16
Int8UInt8

Aclarar que para los tipos Int64 y UInt64 existen los “alias” Int y UInt, es decir que cuando escribimos Int es como si escribiésemos Int64 y cuando escribimos UInt es como si escribiésemos UInt64.

Explicado lo anterior y entendida su lógica podemos deducir que el tipo de dato correcto para representar una edad humana sería la de Int8, básicamente porque es el más pequeño de todos y donde nuestro rango de edad se encuentra perfectamente dentro de los límites. Al menos mientras que los humanos no vivan más de 255 años, que sería el número decimal más grande que podríamos representar con 8 bits.

Nuestro ejemplo actualizado quedaría así:

var edad: UInt8 = 30

Proseguimos a imprimir los limites:

print("\nUInt8 (8 bits explicitly specified) minimum value: \(UInt8.min)")
print("UInt8 (8 bits explicitly specified) maximum value: \(UInt8.max)")

La salida en pantalla sería:

UInt8 (8 bits explicitly specified) minimum value: 0

UInt8 (8 bits explicitly specified) maximum value: 255

En este punto corroboramos que nuestro razonamiento estaba bien formulado y ahora tenemos una variable más acorde al tipo de dato que gestionará ese segmento de memoria. Ahora nos toca a nosotros mediante otros métodos validar que los valores pasados a esta variable jamás exceda los 255.

Constantes

Las constantes son muy similares a las variables, de hecho la única diferencia es que una vez que las inicializamos con un valor ya no podemos modificarlo. Serían como variables de solo lectura, o mejor dicho son segmentos de memoria de solo lectura, valores constantes que no cambian.

Estas constantes se declaran con la palabra clave let

let name = "Archimedes of Syracuse"

let edad: UInt8  = 30

La primera constante que hemos declarado ha sido inicializada con un valor de tipo String, en este caso un nombre y la segunda con un número haciendo referencia a una edad. También aplica a las constantes la inferencia de tipo como podemos observar en la constante name, que en este caso es de tipo String.

Ahora, los nombres no cambian pero la edad sí, por este motivo lo más lógico es que edad continue siendo una variable y con esto lo que busco es que se entienda que hay datos que siempre son los mismos, como la cantidad de pisos del tú edificio, el número PI, la constante Euler, el número de ruedas de un carro, el número de patas de tu perro o gato… etc.

Otros datos están dados a cambiar dada su naturaleza, como la edad, el año en que estamos, la cantidad de ciudadanos de un país, el costo de un producto… y así vemos como desde la programación necesitamos una manera de representar esta mutabilidad de los datos.

Dicho lo anterior creo que es mejor asociar a Archimedes con el número PI que con una edad de 30 años:

let name = "Archimedes of Syracuse"

// let edad: UInt8 = 30

let piNumber = Double.pi

Como el bueno de Archimedes lleva años muerto dudo que se cambie el nombre y aún cuando el número PI puede ser modificado si se calculan más decimales pues la API de Swift se actualizará y el código que hemos declarado seguirá siendo válido.

Esta última es la razón por la cual usamos Double.pi en lugar de un valor literal especificado por nosotros y que luego sí tendríamos que modificar en caso de necesitar más precisión.

Cuando declaramos una constante le informamos al compilador de que ese segmento de memoria una vez inicializado ya no se modificará su valor y esto permite al compilador optimizar el código basado en esta característica.

Inicialización de Constantes

Las constantes se pueden inicializar de dos formas, al momento de la declaración o mediante un constructor o inicializador como se les llama en Swift.

Ya se que este tema no iría en este punto pero creo importante que entiendan el panorama completo. Digamos así rápido que una clase o una estructura es un bloque de código en el cual definimos los comportamientos de cierto objeto o entidad.

struct Student {
    
    let name: String
    var age: Int
    
    let id = UUID().uuidString
    
    init(withName name: String, andAge age: Int) {
        
        self.name = name
        self.age = age
        
    } // init
    
} // Student

En este último ejemplo vemos un ejemplo de lo antes comentado. Tenemos una estructura de nombre Student (Estudiante) en representación de un objeto que a su vez funge como un estudiante en nuestra aplicación y en nuestra base de datos. En este objeto se recogen los datos de nombre, edad y un identificador único que se genera automáticamente.

Dentro de nuestra estructura tenemos dos constantes y una variable, la constante name donde almacenaremos el nombre, la variable age correspondiente a la edad y por último la constante id que igualamos desde su declaración a un identificador.

Luego mediante el bloque init igualamos la constante name y la variable age una vez que se crea una instancia de este objeto y se asigna el segmento de memoria asociado a este.

Es decir que tenemos dos manera de inicializar una constante, un ejemplo sería la constante id que igualamos a su valor una vez que la definimos y el otro caso sería el de la variable name que se le asigna un valor mediante el inicializador.

Para no dejarlos con la curiosidad nuestra estructura Student se puede instanciar de esta manera:

var currentStudent = Student(withName: "Paco", andAge: 20)

print("\n\nStudent ID: \(currentStudent.id)\nStudent Name: \(currentStudent.name)\nStudent Age: \(currentStudent.age)")

La salida en pantalla de este código sería la siguiente:

Student ID: F68B26AA-1CBC-4795-9582-1137735157FA

Student Name: Paco

Student Age: 20

Con la función print mostramos los datos que hemos pasado a nuestro objeto currentStudent, y básicamente digamos que cuando instancias un objeto asignamos un espacio de memoria a todas las constantes y variables miembro de nuestra estructura, más la lógica asociada a las funciones miembro que en este caso no hemos definido ninguna.

Todo esto que hemos comentado y que quizás no se entienda del todo en un inicio lo iremos viendo en más profundidad en la medida que vayamos desarrollando otros temas.

Nombre de Variables y Constantes

Los nombres de las variables y constantes pueden contener prácticamente cualquier carácter, incluyendo caracteres Unicode.

Veamos algunos ejemplos válidos:

let π = Double.pi

let 你好 = "你好世界"

var number7 = "Número al final del nombre."
var number4IsEven = "Número en el medio del nombre."

let 🐶🐮 = "Dog and Cow"

Ahora bien, y según como indica la propia documentación los nombres ya sea de una constante o variable no pueden jamás contener caracteres de espacio en blanco, símbolos matemáticos, flechas, valores Unicode escalares de uso privado o bien caracteres de línea o caja.

Tampoco pueden comenzar con un número, aunque los números pueden incluirse en otros lugares dentro del nombre, dígase al medio o al final tal y como vemos en el anterior ejemplo.

Algo que creo bastante evidente y que básicamente es así en prácticamente todo los lenguajes de programación es lo siguiente:

Una vez que hayamos declarado una constante o variable de un cierto tipo, no podemos declararlo de nuevo con el mismo nombre, o cambiar el tipo de dato para almacenar valores de un tipo diferente.

Tampoco podemos convertir una constante en variable y viceversa.

Aclaración sobre las Anotaciones

En Swift las anotaciones no son más que la declaración de un topo de dato a una variable o tipo. Es decir, esto es una anotación:

let constante: String = “Hello”

El segmento en negrita (:String) sería la anotación que hemos agregado a esa constante y que informa al compilador sobre el tipo de dato.

Me gustaría clarar que los ejemplos que he puesto más arriba sobre Int8 o UInt8 han sido con el objetivo que se entienda que es una variable o una constante y como funciona la asignación de esos segmentos de memoria en dependencia del tipo de dato.

En Swift recomiendan usar Int aún cuando vayamos a almacenar números pequeños y que jamás serán negativos. Esta recomendación tiene o tendría sentido desde los siguientes enfoques:

  • Seguridad: Int8 puede dar lugar a errores si el programador no hace las validaciones necesarias antes de almacenar un número introducido por el usuario.
  • ¿El compilador optimiza esas variables o constantes declaradas como Int y las convierte a Int8 o UInt8?

Lo que sucede es que la segunda opción no es cierta, el compilador no puede determinar en la mayoría de los casos este tipo de cuestiones. Por ende la recomendación solo está enfocada en la primera opción que no es más que un tema de seguridad y evitar que en muchos casos haya que estar haciendo casting de datos o validando los valores.

Dicho esto el uso de segmentos de tipo Int8, Int16 o UInt8 queda a valoración del desarrollador.

Sentido Común

Conclusiones

Como podemos constatar Swift es un lenguaje con una sintaxis muy sencilla y limpia, se trabaja de una manera muy fluida, y a los que hemos trabajado con Objetive-C nos resulta un alivio más bienvenido.

En mi caso que he trabajado con C y C++ la sintaxis que Objetive-C me resultaba la peor de todas, muy enrevesada por gusto y quizás por eso valoro tanto la objetividad y el pragmatismo de Swift.

Códigos de Ejemplo

Los ejemplos de código vistos en este artículo los puedes encontrar en la cuenta de KodigoSwift en GitHub y específicamente en el repositorio asociado a este artículo.

Falta aún mucho por aprender en nuestro camino a convertirnos en iOS Developer. Suscríbete a nuestra lista de correo mediante el formulario en el panel derecho y síguenos en nuestras redes sociales. Mantente así al tanto de todas nuestras publicaciones futuras.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!

Josué V. Herrera

Desarrollador Swift / iOS. Llevo en el mundo de la informática más de 15 años, trabajado con varios lenguajes de programación y frameworks como C++ / Qt, Java, C#, Bash Scripting, entre otros. También he fungido como administrador de redes, bases de datos y sistemas Linux (y algunos BSD) desde 2005 hasta la actualidad. Me inicio en el mundo Swift y el desarrollo Apple desde el lanzamiento del lenguaje, actualmente me dedico al desarrollo de aplicaciones iOS, a montar VPS (Virtual Private Server) en varias configuraciones y a escribir artículos técnicos. Soy “Experto en Administración y Seguridad de Redes” por la Universidad Tecnológica Nacional FRVM de Córdoba, Argentina.

Publicaciones relacionadas

RECIBE CONTENIDO SIMILAR EN TU CORREO

RECIBE CONTENIDO SIMILAR EN TU CORREO

Suscríbete a nuestra lista de correo y mantente actualizado con las nuevas publicaciones.

Se ha suscrito correctamente!

Cerrar

Bloqueador de anuncios detectado

Por favor, considere ayudarnos desactivando su bloqueador de anuncios