Advertisement
  1. Code
  2. Mobile Development
  3. iOS Development

Swift desde Cero: Una Introducción a Clases y Estructuras

Scroll to top
Read Time: 13 min
This post is part of a series called Swift From Scratch.
Swift From Scratch: Closures
Swift From Scratch: Inheritance and Protocols

() translation by (you can also view the original English article)

En los artículos anteriores de esta serie, cubrimos los básicos del lenguaje de programación Swift. Si seguiste a la par, deberías tener ahora un sólido entendimiento de variables, constantes, funciones y cierres. Ahora es tiempo de usar lo que hemos aprendido hasta ahora y aplicar ese conocimiento a los conceptos orientados a objetos disponibles en Swift.

Para entender los conceptos discutidos en este tutorial, es importante que tengas un entendimiento básico de programación orientada a objetos. Si no estás familiarizado con clases, objetos y métodos, entonces te recomiendo que primero leas estos temas antes de continuar con este artículo.

1. Introducción

En este artículo, vamos a explorar la construcción fundamental de bloques de programación orientada a objetos en Swift, clases y estructuras. En Swift, las clases y estructuras se sienten y comportan muy similar, pero hay un número de diferencias clave que necesitas entender para evitar trampas comunes.

En Objective-C, las clases y estructuras son muy diferentes. Esto no es verdad para Swift. En Swift, por ejemplo, ambas clases y estructuras pueden tener propiedades y métodos. A diferencia de estructuras C, las estructuras en Swift pueden ser extendidas y conformadas a protocolos.

La pregunta es  "¿Cuál es la diferencia entre clases y estructuras?" Revisitaremos esta pregunta después en este artículo. Primero exploremos cómo luce una clase en Swift.

2. Terminología

Antes de comenzar a trabajar con clases y estructuras, me gustaría clarificar unos cuántos términos comúnmente usados en programación orientada a objetos. Los términos clases, objetos e instancias frecuentemente confunden a la gente que es nueva en la programación orientada a objetos y es por eso que es importante que sepas cómo Swift usa estos términos.

Objetos e Instancias

Una clase es un plano o plantilla para una instancia de esa clase. El término objeto es frecuentemente usado para referirse a una instancia de una clase. En Swift, sin embargo, las clases y estructuras son muy similares y por lo tanto más fácil y menos confuso de usar el término instancia para ambas clases y estructuras.

Métodos y Funciones

Anteriormente en la serie, trabajamos con funciones. En el contexto de clases y estructuras, usualmente nos referimos a las funciones como métodos. En otras palabras, los métodos son funciones que pertenecen a una clase o estructura particular. En el contexto de clases y estructuras, puedes usar ambos términos intercambiablemente ya que todo método es una función.

3. Definiendo una Clase

Mojémonos los pies definiendo una clase. Inicia Xcode y crea un nuevo playground. Remueve los contenidos del playground y agrega la siguiente definición de clase.

1
class Person {
2
    
3
}

La palabra clave class indica que estamos definiendo una clase llamada Person. La implementación de la clase está envuelta en un par de llaves. Incluso aunque la clase Person no es muy útil en su forma actual, es una clase Swift apropiada y funcional.

Propiedades

Como en la mayoría de los lenguajes de programación orientada a objetos, una clase puede tener propiedades y métodos. En el ejemplo actualizado de abajo, definimos tres propiedades:

  • firstName, una propiedad variable de tipo String?
  • lastName, una propiedad variable del tipo String?
  • gender: una propiedad constante de tipo String
1
class Person {
2
    var firstName: String?
3
    var lastName: String?
4
    let gender = "female"
5
}

Como ilustra el ejemplo, definir propiedades en una definición de clase es muy similar a definir variables y constantes regulares. Usamos la palabra clave var para definir una propiedad variable y la palabra clave let para definir una propiedad constante.

Las propiedades de arriba también son conocidas como propiedades almacenadas. Después en esta serie, aprenderemos sobre propiedades computadas. Como el nombre implica, las propiedades almacenadas son propiedades almacenadas por la instancia de la clase. Son muy similares a las propiedades en Objective-C.

Es importante notar que cada propiedad almacenada necesita tener un valor después de inicialización o ser definida como un tipo opcional. En el ejemplo de arriba, damos a la propiedad gender un valor inicial de "female". Esto le dice a Swift que la propiedad género es de tipo String. Después en este artículo, echaremos un vistazo a la inicialización a más detalle y exploraremos cómo se ata con inicializar propiedades.

Incluso aunque definimos la propiedad gender como una constante, es posible cambiar su valor durante la inicialización de una instancia de Person. Una vez que la instancia ha sido inicializada, la propiedad gender ya no puede ser modificada ya que definimos la propiedad como propiedad constante con la palabra clave let. Esto se volverá más claro después en este artículo cuando discutamos inicialización.

Métodos

Podemos agregar comportamiento o funcionalidad a una clase a través de funciones o métodos. En muchos lenguajes de programación, el término método es usado en lugar de una función en el contexto de clases e instancias. Definir un método es casi idéntico a definir una función. En el siguiente ejemplo, definimos el método fullName en la clase Person.

1
class Person {
2
    var firstName: String?
3
    var lastName: String?
4
    let gender = "female"
5
    
6
    func fullName() -> String {
7
        var parts: [String] = []
8
        
9
        if let firstName = self.firstName {
10
            parts += [firstName]
11
        }
12
        
13
        if let lastName = self.lastName {
14
            parts += [lastName]
15
        }
16
        
17
        return " ".join(parts)
18
    }
19
}

El método fullName está anidado en la definición de la clase. Esta no acepta parámetros y devuelve un String. La implementación del método fullName es sencillo. A través de enlazamiento opcional, que discutimos anteriormente en esta serie, accedemos a los valores almacenados en las propiedades firstName y lastName.

Almacenamos el nombre y apellido de la instancia Person en un arreglo y unimos las partes con un espacio. La razón para esta implementación algo incómoda debería ser obvia, el nombre y el apellido pueden estar en blanco, por lo que ambas propiedades son de tipo String?.

Instalación

Hemos definido una clases con unas cuántas propiedades y un método. ¿Cómo creamos una instancia de la clase Person? Si estás familiarizado con Objective-C, entonces vas a amar lo conciso del siguiente código.

1
let john = Person()

Instanciar una instancia de una clase es muy similar a invocar una función. Para crear una instancia, el nombre de la clase es seguido por un par de paréntesis, el valor de retorno es asignado a una constante o variable.

En nuestro ejemplo, la constante john ahora apunta a una instancia de la clase Person. ¿Esto significa que no podemos cambiar ninguna de sus propiedades? El siguiente ejemplo responde esta pregunta.

1
john.firstName = "John"
2
john.lastName = "Doe"
3
john.gender = "male"

Podemos acceder a las propiedades de una instancia usando la conveniencia de la sintaxis de punto. En el ejemplo, establecemos firstName a "John", lastName a "Doe", y gender a "male". Antes de que saquemos cualquier conclusión basados en el ejemplo de arriba, necesitamos revisar cualquier error en el playground.

Establecer firstName y lastName no parece causar ningún problema. Asignar "male" a la propiedad gender, sin embargo, resulta en un error. La explicación es simple. Incluso aunque john es declarado como constante, eso no nos previene de modificar la instancia Person. Hay una advertencia sin embargo, solo las propiedades variables pueden ser modificadas después de inicializar una instancia. Las propiedades que están definidas como constante no pueden ser modificadas después de la inicialización.

Nota que enfaticé después en la oración anterior. Una propiedad constante puede ser modificada durante la inicialización de una instancia. Mientras que la propiedad gender no debería ser cambiada una vez que la instancia Person ha sido creada, las clases no serían muy útiles si solo pudiéramos instanciar instancias femeninas de Person. Hagamos la clase Person un poco más flexible.

Inicialización

La inicialización es un paso en el tiempo de vida de una instancia de una clase o estructura. Durante la inicialización, preparamos la instancia para usar poblando sus propiedades con valores iniciales. La inicialización de una instancia puede ser personalizada implementando un inicializador, un tipo especial de método. Agreguemos un inicializador a la clase Person.

1
class Person {
2
    var firstName: String?
3
    var lastName: String?
4
    let gender = "female"
5
    
6
    init() {
7
        gender = "male"
8
    }
9
}

Nota que el nombre del inicializador, init, no es precedido por la palabra clave func. En contraste a los inicializadores en Objective-C, un inicializador en Swift no devuelve la instancia que está siendo inicializada.

Otro detalle importante es cómo establecemos la propiedad gender con un valor inicial. Incluso si la propiedad gender es definida como una propiedad constante, podemos establecer su valor en el inicializador. Establecemos la propiedad gender usando la propiedad nombre, pero también está bien ser más explícito y escribir lo siguiente:

1
init() {
2
    self.gender = "male"
3
}

En el ejemplo de arriba, self se refiere a la instancia que está siendo inicializada. Esto significa que self.gender se refiere a la propiedad gender de la instancia. Podemos omitir self, como en el primer ejemplo, porque no hay confusión de a qué propiedad nos estamos refiriendo. Aunque este no siempre es el caso. Déjame explicar a qué me refiero.

Parámetros

En muchas situaciones, quieres pasar valores iniciales al inicializador para personalizar la instancia que estás inicializando. Esto es posible creando  un inicializador personalizado que acepte uno o más argumentos. En el siguiente ejemplo, creamos un inicializador personalizado que acepta un argumento, gender, de tipo String.

1
init(gender: String) {
2
    self.gender = gender
3
}

Ahí hay dos cosas que notar. Primero, se nos requiere acceder a la propiedad gender a través de self.gender para evitar ambigüedad ya que el nombre del parámetro local es igual a gender. Después, incluso aunque no hemos especificado un nombre de parámetro externo, Swift por defecto crea un parámetro externo que es igual al nombre de parámetro local. El resultado es el mismo que si prefijaramos el parámetro gender con un símbolo #.

En el siguiente ejemplo, instanciamos otra instancia Person invocando el inicializador personalizado que acabamos de definir.

1
let bart = Person(gender: "male")
2
3
println(bart.gender) // Outputs "male"

Incluso aunque el valor inicial de la propiedad gender es establecido a "female" en la definición de clase, pasando el valor para el parámetro gender podemos asignar una variable personalizada a la propiedad constante gender durante la inicialización.

Múltiples Inicializadores

Como en Objective-C, una clase o estructura puede tener múltiples inicializadores. En el siguiente ejemplo, creamos dos instancias Person. En la primera línea, usamos el inicializador por defecto. En la segunda línea, usamos el inicializador personalizado que definimos anteriormente.

1
let p1 = Person()
2
let p2 = Person(gender: "male")

4. Definiendo una Estructura

Las estructuras son sorprendentemente similares a las clases, pero hay unas cuantas diferencias clave. Comencemos definiendo una estructura básica.

1
struct Wallet {
2
    var dollars: Int
3
    var cents: Int
4
    
5
    init() {
6
        dollars = 0
7
        cents = 0
8
    }
9
}

A primera vista, la única diferencia es el uso de la palabra clave struct en vez de la palabra clave class. El ejemplo también nos muestra una aproximación alternativa para proporcionar valores iniciales a propiedades. En vez de establecer un valor inicial para cada propiedad, podemos darle a las propiedades un valor inicial en el inicializador de la estructura. Swift no arrojará un error, porque también inspecciona el inicializador para determinar el valor inicial---y tipo---de cada propiedad.

5. Clases y Estructuras

Podrías comenzar a preguntarte cuál es la diferencia entre clases y estructuras. A primera vista, lucen idénticos en forma y función, con la excepción de las palabras clave class y struct. Hay un número de diferencias clave sin embargo.

Herencia

Las clases soportan herencia mientras que las estructuras no. El siguiente ejemplo ilustra esto. El diseño de patrón de herencia es indispensable en la programación orientada a objetos y, en Swift, una diferencia clave entre clases y estructuras.

1
class Person {
2
    var firstName: String?
3
    var lastName: String?
4
    let gender = "female"
5
    
6
    init(gender: String) {
7
        self.gender = gender
8
    }
9
}
10
11
class Student: Person {
12
    var school: String?
13
}
14
15
let student = Student(gender: "male")

En el ejemplo de arriba, la clase Person es el padre o superclase de la clase Student. Esto significa que la clase Student hereda las propiedades y comportamiento de la clase Person. La última línea ilustra esto. Inicializamos una instancia Student invocando el inicializador personalizado que definimos anteriormente en la clase Persona.

Copiando y Referenciando

El siguiente concepto es probablemente el más importante en Swift que aprenderás hoy, la diferencia entre valor y tipos de referencia. Las estructuras son tipos de valores, lo que significa que son pasados por valor. Un ejemplo ilustra mejor este concepto.

1
struct Point {
2
    var x: Int
3
    var y: Int
4
    
5
    init(x: Int, y: Int) {
6
        self.x = x
7
        self.y = y
8
    }
9
}
10
11
var point1 = Point(x: 0, y: 0)
12
var point2 = point1
13
14
point1.x = 10
15
16
println(point1.x) // Outputs 10

17
println(point2.x) // Outputs 0

Definimos una estructura, Point, para encapsular los datos para almacenar una coordenada en un espacio bi-dimensional. Instanciamos point1 con x igual a 0 y y igual a 0. Asignamos point1 a point2 y establecemos la coordenada x de point1 a 10. Si damos salida a la coordenada x de ambos puntos, descubrimos que no son iguales.

Las estructuras son pasadas por valor mientras que las clases son pasadas por referencia. Si planeas continuar trabajando con Swift, necesitas entender la declaración anterior. Cuando asignamos point1 a point2, Swift creó una copia de point1 y la asignó a point2. En otras palabras, point1 y point2 cada uno apunta a una instancia diferente de la estructura Point.

Repitamos ahora este ejercicio con la clase Person. En el siguiente ejemplo, instanciamos una instancia Person, establecemos sus propiedades, asignamos person1 a person2, y actualizamos la propiedad firstName de person1. Para ver qué significa pasar por referencia para clases, sacamos el valor de la propiedad firstName en ambas instancias Person.

1
var person1 = Person(gender: "female")
2
3
person1.firstName = "Jane"
4
person1.lastName = "Doe"
5
6
var person2 = person1
7
8
person1.firstName = "Janine"
9
10
println(person1.firstName)
11
println(person2.firstName)

El ejemplo prueba que las clases son tipos de referencia. Esto significa que person1 y person2 refieren a o hacen referencia a la misma instancia Person. Asignando person1 a person2, Swift no crea una copia de person1. La variable person2 apunta a la misma instancia Person a la que person1 está apuntando. Cambiar la propiedad firstName de person1 también afecta la propiedad firstName de person2, debido a que están referenciando a la misma instancia Person.

Como mencioné varias veces en este artículo, las clases y estructuras son muy similares. Lo que separa a las clases y estructuras es muy importante. Si los conceptos de arriba no son claros, entonces te aliento a leer el artículo una vez más para dejar que los conceptos que cubrimos se hundan.

Aprende Más en Nuestro Curso de Programación Swift

Si estás interesado en llevar tu educación Swift al siguiente nivel, puedes echar un vistazo a nuestro curso completo sobre desarrollo Swift.

Conclusión

En esta entrega de Switch desde Cero, hemos comenzado a explorar los básicos de programación orientada a objetos en Swift. Las clases y estructuras son los bloques de construcción fundamentales de la mayoría de proyectos Swift y aprenderemos más en las siguientes lecciones de esta serie.

En el siguiente artículo, continuamos nuestra exploración de clases y estructuras echando un vistazo más cercano a las propiedades y herencia.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.