Swift – Tipos de Datos

En esta ocasión vamos a aprender sobre los distintos tipos de datos, la inferencia y las anotaciones de tipos en el lenguaje Swift, veremos ejemplos y comentaremos alguno que otro matiz sobre este tema.

Vamos poco a poco y primero respondamos la siguiente pregunta.

¿Qué es un tipo de dato?

Continuando con el ejemplo del artículo anterior: sobre una mesa en representación de la memoria RAM, donde los platos, basos, etc serían representaciones de variables o constantes, y como cada uno de estos es más óptimo que otro para cierto tipo de alimentos.

Bueno, un tipo de dato sería el análisis que hacemos sobre un tipo de alimento, cuando vamos a servir la mesa y tenemos sopa la clasificamos como un semi-líquido y con esto el compilador le informa al sistema operativo, o el sentido común le informa a nuestro cerebro, que debe de verterla sobre un plato hondo, si es agua o jugo sobre un vaso y es un pavo pues sobre una fuente bien grande.

En dependencia del tipo de dato es el segmento de memoria, tanto en tamaño como en optimización.

Inferencia de tipos

Pero ¿cómo funciona esto de los tipos de datos? en el ejemplo del artículo anterior solo especificábamos la palabra clave var o let para especificar si queríamos crear una variable o una constante. ¿Cómo sabe el compilador si nos referimos a una variable o constante de tipo numérico o una cadena de texto?

En los ejemplos anteriores no especificamos el tipo de dato porque en Swift contamos con una tecnología / feature llamada Inferencia de Tipos, y esto consiste en que Swift infiere el tipo de dato analizando el valor que hemos asociado.

let carBrand = "Tesla"
let cardModel = "S"
let carDoors = 4
var carOdometer = 10000

En este ejemplo el compilador infiere que las dos primeras constantes son de tipo String ya que este es el dato que le estamos asignando. Mientras que la constante y la variable restante son de tipo numérico o Int (viene de integer).

Anotaciones de tipo

Ahora, si contamos con la inferencia de tipos, ¿Qué sentido tiene declarar explícitamente el tipo de dato? ¿Cómo se hace esto?

Pues la mayoría de las veces no es necesario declarar el tipo de una variable o constante, y lógicamente hay otras en las que sí. Para declarar un tipo de dato nos valemos de identificadores de tipo, nombres con los cuales hacemos referencia a los tipos de datos. Veamos:

let carBrand: String = "Tesla"
let cardModel: String = "S"
let carDoors: Int = 4
let carLength: Double = 195.9
let carWidth: Double = 77.3
var carOdometer: Int = 10000
var carScheduledMaintenances = [Date]()
var carInsured: Bool = true

Hemos modificado el ejemplo anterior y hemos expandido las variables y constantes agregándoles el tipo de dato, siguiendo la sintaxis:

var / let nombre: tipo = valor

Donde el tipo sería la manera de especificar al compilador el tipo de dato de nuestra variable. String hace referencia a las cadenas de texto, Int a los enteros, double a los números con decimales, Bool para valores boleanos.

En el caso de Date y scheduledMaintenances la hemos inicializado con un arreglo de datos vacio de tipo Date, no hemos usado la sintaxis que estamos comentando porque sería demasiado reiterativo ya que la estamos aplicando en el valor. Los arreglos de datos los veremos más adelante.

¿No es demasiado?

Para entender mejor el tema de las anotaciones de tipo y la asignación de espacio a estas variables hagamos un análisis:

Imaginemos que tenemos una variable edad, la siguiente:

var edad = 25

Por la inferencia de datos esta variable será de tipo Int. Pero ¿cuánto podemos admacenar en una variable Int? Veamos cuales son los márgenes de un tipo de dato Int, es decir cuanto podemos variar nuestro valor de 25 y aún ser almacenado por un tipo Int.

Vamos a valernos de la función print para mostrar un texto en la salida estandar (la terminal), este texto nos mostrará el valor mínimo y máximo que puede almacenar una variable o constante de tipo Int.

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 los rangos del tipo de dato Int (en dependencia de si nuestro dispositivo es de 32 bits o 64 bits), que van desde ese valor mínimo hasta el máximo, en otras palabras: nuestro valor de 25 puede ser modificado dentro de esos límites negativos y positivos.

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

Siempre que no hayan limitantes (de librerías, frameworks u otros factores) podemos usar una variante positiva del tipo Int, es decir que solo podemos almacenar números positivos. Me refiero a UInt (la U viene del inglés Unsigned, que significa «sin signo»), y sí, en efecto esta sería una solución mucho más coherente con nuestra necesidad real.

¿Cuándo no usar la Inferencia de Tipo?

Ahora veremos un ejemplo donde especificar el tipo de dato nos será util. Declaremos nuevamente la variable edad pero ahora declarémosla de tipo UInt, así:

var edad: UInt = 25

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), de no hacerlo por defecto sería de tipo Int.

Basicamente 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 = 25

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.

Tipos de Datos

Luego de esta introducción veamos los tipos de datos con un poco más de detalles y de manera individual.

Int

Aquí tenemos al clásico Int que nos viene a ayudar con el almacenaje de números enteros, sobre este pudiéramos decir que en la mayoría de los casos no es necesario elegir un tamaño específico de número entero para utilizar en nuestro código como quizás hacemos en C# u otros lenguajes.

Swift ofrece un tipo entero adicional, Int, que tiene el mismo tamaño que el tamaño de la plataforma actual, en la que estemos trabajando o en la que esté ejecutándose nuestro código:

  • En una plataforma de 32 bits, Int es del mismo tamaño que Int32.
  • En una plataforma de 64 bits, Int es del mismo tamaño que Int64.

A menos que usted necesite trabajar con un tamaño específico de número entero, utilice siempre Int para valores enteros en el código. Esto ayuda a la consistencia y la interoperabilidad del código. Incluso en las plataformas de 32 bits, Int puede almacenar cualquier valor entre -2147483648 y 2147483647, y es lo suficientemente grande para muchos rangos enteros.

Double

Los números de coma o punto flotante no son más que aquellos que constan de un componente fraccional, como 3.14159, 0.1 y -273.15.

Los tipos de datos de coma flotante pueden representar una gama mucho más amplia de valores que los tipos enteros, por lo que pueden almacenar números mucho más grandes o más pequeños que los que se pueden almacenar en un Int. Swift ofrece dos tipos de números de punto flotante:

  • Double representa un número de coma flotante de 64 bits.
  • Float representa un número de coma flotante de 32 bits.

Double tiene una precisión de al menos 15 dígitos decimales, mientras que la precisión de Float puede ser tan pequeña como 6 dígitos decimales. El tipo de punto flotante apropiado a utilizar dependerá de la situación, de la naturaleza y el alcance de los valores que se necesita. En situaciones en las que uno u otro tipo sería apropiado, se aconseja usar Double.

Bool

Swift tiene un tipo de dato llamado Bool para la representación de valores booleanos. Es decir estados binarios, valores que puede ser o true o false.

Veamos como declarar este tipo de variables o constante:

let limonAgrio = true
let tamarindoDulce = false

var landingGearUp: Bool = false
var greenLightOn = Bool(true)

Los tipos de las variables limonAgrio y tamarindoDulce se han deducido como Bool ya que las hemos inicializado con valores literales booleanos. En los otros dos ejemplos somos explicitos con el tipo de dato.

Al igual que con Int y Double no es necesario especificar el tipo de dato a la hora de declarar una variable de tipo Bool, siempre y cuando le asociemos el valor adecuado desde un inicio.

La inferencia de tipos ayuda a que el código sea más conciso y legible cuando se inicialicen constantes o variables con valores cuyo tipo ya es conocido.

String

Una cadena de texto es una secuencia de caracteres, como lo es la oración “Hola, mundo!” o la palabra “albatros”. En Swift este tipo de dato está representados por el tipo String, el cual nos proporciona una manera rápida (y compatible con Unicode) de trabajar con texto desde nuestro código.

Es bien simple e intuitivo, pasemos a ver algunos ejemplos de como declarar constantes / variables de tipo String:

var name: String = ""
var emptyString = ""
var anotherEmptyString = String()

let greeting = "Hola"

let ohmSign = "\u{03A9}"

let multiline = """

 Cuando declaramos una variable o constante String de esta manera,
 podemos segmentar el texto en varias líneas.

 Dejar espacios entre ellas y aplicar:       tabulaciones!

 En ocaciones puede reultar muy conveniente.
 
"""

Expliquemos una a una cada una de estas declaraciones:

  • En la primera línea declaramos explicitamente que la variable es de tipo String y la inicializamos con el valor «», es decir con una cadena de texto vacia.
  • Luego declaramos otra variable, en esta ocación no especificamos el tipo de dato ya que el compilador lo infiere a través del valor que le hemos asignado «».
  • El tercer ejemplo es similar al primero, nos valemos del tipo de dato String y en este caso de su constructor para asignar un texto vacio a la variable.
  • A partir del cuarto ejemplo ya definimos constantes con contenido asociado. En este caso es un saludo, una cadena de texto simple, un «Hola».
  • El nombre de la constante del quinto ejemplo lo dice todo. Aquí hemos declarado una constante de tipo String con la representación Unicode del signo de ohm Ω.
  • En el último ejemplo mostramos como declarar una variable de tipo String inicializándola con las tres comillas. Esto nos permite más felixibilidad a la hora de formatear el texto. Podemos dejar líneas en blanco, aplicar tabulaciones, saltos de línea, todo de una manera bien sencilla y visual.

Es decir que como mismo el tipo de dato Int nos permite almacenar números pues el tipo de dato String nos permite almacenar textos. Si quisiéramos almacenar nuestro nombre haríamos algo tan simple como esto:

let name = "Josué"

print("Mi nombre es: \(name)")

la salida en pantalla sería:

Mi nombre es: Josué

Tuple

Este tipo de dato puede estar conformado por una lista (separada por comas) de ceros o más tipos de datos, encerrados todos dentro de paréntesis.

let httpERROR: (Int, String) = (404, "The requested resource could not be found...")

println("ERROR: \(httpERROR.0) - \(httpERROR.1)")

En este ejemplo creamos una tupla constante de nombre “httpERROR”, conformada por dos valores, Int y String. En la siguiente línea hacemos uso de nuestra tupla desde la función println, haciendo referencia al primer y segundo valor mediante los indices de 0 y 1, imprimiendo los valores antes declarados.

Las Tuplas entre sus ventajas de uso nos permiten, por ejemplo, que al ser retornadas por una función estemos devolviendo varios valores de una vez, pudiendo también especificar nombres de elementos para luego hacer referencia a sus valores independientes. Estos elementos son identificadores, en su declaración seguidos por dos puntos (:).

//: Playground - noun: a place where people can play

import UIKit

func minMax (array: [Int]) -> (min: Int, max: Int) {

    var currentMin = array[0]
    var currentMax = array[0]

    for value in array[1..<array.count] {

        if value < currentMin {

            currentMin = value

        } else if value > currentMax {

            currentMax = value

        } // else if

    } // for

    return (currentMin, currentMax)

} // minMax

let bounds = minMax([8, -6, 2, 109, 3, 71])

print("Min is \(bounds.min) and Max is \(bounds.max)")

La salida en pantalla es la siguiente:

Min is -6 and Max is 109

De la línea 5 a la 26 declaramos la función minMax. Esta función recibe un arreglo de tipo int, con estos valores analiza cual es el más pequeño y el más grande, llegado a este punto devuelve una Tupla con estos dos valores.

En la línea 28 hacemos una llamada a nuestra función, especificando nuestro arreglo a trabajar y capturando la tupla devuelta en la constante “bounds”, a la que luego hacemos referencia en la línea 30 para imprimir los valores devueltos.

Lo curioso y digno de mención, sobre esta referencia, es que la hacemos a los nombres de elementos min y max, aquellos que especificamos en la línea 5 cuando establecimos el retorno de nuestra función, específicamente en esta declaración:

... -> (min: Int, max: Int)

El operador “flecha” se usa para declarar el valor de retorno, seguido a este la tupla que devolveremos, donde nombramos a cada elemento para luego poder hacer referencia a los mismos de una manera mucho más cómoda y menos propensa a errores, quedando un código muy legible y claro.

Conclusiones

Sin los tipos de datos no solo jamás hubiera existido la computación sino que tampoco tendría sentido la programación, y menos aún nosotros mismos que somos seres inteligente con una memoria histórica, tanto cultural como científica.

Los datos, como los tipos en los cuales podríamos clasificarlos, nos llegan todos desde el mundo real. Gracias a ellos existe la lengua hablada y escrita, así como las matemáticas, nos podemos comunicar, nos asociamos nombres propios, llevamos la cuenta de nuestra edad y también tenemos un salario.

Un nombre es una cadena de caracteres de tipo String, la edad es un valor numérico de tipo Int y un salario es un valor numérico de tipo Double. Esta información durante siglos solo persistia en nuestros cerebros y en los de aquellos que nos recordaban, y luego a favor de ser más precisos; en documentos de papel.

Los tipos de datos, tanto los básicos como los más complejos, nos permiten almacenar la información y los estados que deseamos representar en nuestras aplicaciones.

Sin tipos de datos no habría forma de almacenar la información; no existiría una aplicación de calendarios, una aplicación de alarma como tampoco ningún juego, y menos aún bases de datos; la Wikipedia o las redes sociales.

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

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 duda o sugerencia, ya sea errores a corregir o ejemplos a añadir, será más que bienvenida, necesaria!

Deja una respuesta

Su dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para mejorar su experiencia. Asumiremos que está de acuerdo con esto, pero puede optar por no participar si lo desea. Aceptar Leer Más

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!