Home » Cursos » Swift 2 Lección 3: Clases, métodos, structs y herencia
Swift Lección 3

Swift 2 Lección 3: Clases, métodos, structs y herencia

Compra "Aprendiendo Swift 3"

La presente lección está probada y certificada en su funcionamiento con la versión 7 o superior de Xcode y corresponde a la especificación 2 y 2.1 del lenguaje Swift.
Ya conocemos los tipos de variables o constantes y cómo funcionan, también hemos visto los controles de flujo y las variables opcionales, y con esta información podemos montar fácilmente un proyecto con un Playground y trabajar con él.

Con todo ello podemos experimentar y hacer mucho, pero si queremos hacer un programa más complejo (un proyecto completo) nos queda una última cosa que ver: las clases y métodos en Swift. Esas partes esenciales que organizar y que dividen nuestra aplicación en diferentes archivos y dentro de estos en diferentes llamadas (métodos) que nos permiten estructurar todo.

Clases y herencia

La clase es la parte más esencial en que se divide un programa, pudiendo contener una por fichero o varias en uno solo si nos resulta mejor.

Como cualquier lenguaje orientado a objetos, en Swift además podemos utilizar la herencia para estructurar nuestro programa. De esta forma, si hacemos un juego y vamos a tener diferentes tipos de enemigos como un troll, un orco o un espectro, tendremos una clase enemigo que a su vez podrá tener los hijos (o subclases) troll, orco y espectro.

De esta forma, podemos definir métodos que sean comunes a todos los enemigos y que podremos ejecutar en el contexto de cada uno de ellos, aunque luego incluyamos nuevos enemigos.

A su vez, si enemigo es un personaje del juego, también podríamos crear una clase personaje y que enemigo fuera subclase de esta, con lo que aquello común de un personaje, como por ejemplo, saber si está vivo o no, puede ser controlado desde la clase principal e invocado, gestionado o sobrescrito desde cualquiera de sus subclases.

Así de simple hemos definido una clase. Por supuesto, aun nos falta inicializar Enemigo y nos dará error, pero por ahora, de esta forma tenemos una visión clara de lo que estamos hablando. Tenemos un Enemigo con tres propiedades y un Orco, subclase de enemigo, que heredará todos las propiedades de su padre por lo que también tendrá nombre, fuerza y vida.

Clases y structs

Una clase y un struct son muy similares, de hecho, en Swift son casi iguales. Su única diferencia es que mientras una actúa en paso por referencia (la clase) la otra se copia a sí misma en cada nueva asignación (el struct). Los structs, por otro lado, permiten declarar una estructura de datos que no requiere inicializar ninguna de sus variables. Luego, cuando queremos crear una instancia del struct, se nos permite usar un constructor por defecto que nos obliga a dar valor a todos los valores del mismo.

Vamos a crear un struct que represente los valores de fuerza y vida como valores vitales y lo sustituimos en nuestra clase Enemigo.

Ahora sustituimos en la clase los valores y, ahora sí, inicializamos la clase:

Debido a cómo Swift maneja las variables y las constantes en objetos, cuando definimos una instancia de una clase como constante (en let) podremos cambiar las propiedades de esta sin problema, aunque no podremos re-asignar la constante a una nueva clase.

Sin embargo, un struct constante no permite alterar las propiedades de la misma ni tampoco, como pasa con las clases, re-asignar dicha constante a un nuevo struct.

Como vemos, la inicialización de una clase se basa en la palabra clave init, donde pedimos aquellos parámetros que consideremos necesarios para la inicialización. Si tras crear nuestro inicializador, alguna propiedad de la clase no quedara asignada, nos daría error, pues recordemos que en Swift todo ha de ser inicializado con valor en el momento de su creación, incluso las opcionales que llevan un valor vacío y wrapeado que se asigna en el momento de la creación del mismo opcional.

Inicializadores de conveniencia

Ya hemos creado nuestra clase Enemigo, y para inicializarla tenemos que pasarle el nombre del mismo como parámetro, pero también tenemos pasarle la iniciación de un struct que corresponda con sus valores vitales. Esto supondría que tendríamos que crear previamente una variable o constante del struct valoresVitales y luego pasarlo al inicializador de Enemigo.

Esto puede hacerse más simplemente, usando un concepto llamado inicializador de conveniencia. Podemos crear nuevos tipos de inicializadores que nos permitan recibir otros parámetros diferentes que los que la clase requiere de por sí, y que nos permitan construir más cómodamente la instancia.

El concepto es simple: creamos inicializadores alternativos que invoquen al init principal, pero pudiendo pre-procesar los datos. En nuestro caso, creando el struct desde los dos valores Int aislados. De esta forma, cuando queramos crear una nueva clase enemigo como la creada anteriormente, solo tendremos que hacer una única llamada:

Sin duda, una herramienta tremendamente útil pues nos permite inicializar una clase de diferentes formas, en función de los parámetros que queramos y, en este caso concreto, nos ahorra tener que dar pasos intermedios como la creación del struct previo a la creación de la clase donde vamos a usarlo.

Herencia extendida

Imaginemos que queremos crear una nueva clase que extienda por herencia la funcionalidad de una clase padre. Por ejemplo, en el ejemplo que ya hemos trabajo tenemos la clase Enemigo que es padre del Orco, pero queremos que Orco a su vez pueda definir a qué clan pertenece el nuestro. Esta es una característica propia de los orcos, que son enemigos pero que otros enemigos no tienen por qué tener.

Estamos creando una clase Orco que hereda de Enemigo pero extiende sus propiedades añadiendo un nuevo valor clan. Ese valor forma parte de la inicialización de la clase al que luego añadimos uno nuevo de conveniencia que nos permite enviar la fuerza y la vida que conforman los valores vitales por separado.

Es muy importante recordar que los inicializadores de conveniencia, siempre han de llamar a un init de la propia clase y no a la clase padre. Si llamáramos desde este a super.init para invocar a la clase padre (como hemos hecho en el init normal) daría error.

Extendiendo una clase o un struct

En uno de los trucos ya publicados aquí en AppleCoding, teníamos una forma de extender la funcionalidad de una clase String para conseguir que esta nos devolvería lo que se llaman propiedades computadas o calculadas. Valores que se calculan en tiempo real en el momento de la consulta y que depende o tienen que ver con los valores que alberga la propia clase. Estas extensiones alteran y extienden el funcionamiento de cualquiera de ellas a nuestra voluntad, desde una que hayamos creado, hasta cualquiera del sistema.

En el caso del ejemplo del que hablamos, creábamos una nueva propiedad llamada cadenaColor que devolvía una cadena con la frase “Este color es el ” seguido del propio valor de la clase (self). De esta forma, si llamábamos a cualquier String que definiéramos y luego a esta propiedad, obteníamos una cadena compuesta. A un String color con el valor “rojo” al invocar color.cadenaColor nos devolvía “Este color es el rojo”.

En nuestro caso vamos a suponer que queremos obtener el valor de valía de un enemigo, que es el resultado de multiplicar su fuerza por su vida.

Sencillamente definimos una extensión de Enemigo, con una nueva propiedad computada (o calculada). Una propiedad que recupera el valor de fuerza y vida de los valores propios de la clase (self) y devuelve la multiplicación de estos valores.

Ahora solo tenemos que ver el valor de nuestro Enemigo UrukHai que definimos más arriba. Si miramos UrukHai.valia veremos que nos devuelve 1.500, que es la multiplicación de sus valores de fuerza 30 y vida 50.

Vamos ahora a crear un Orco:

Creamos nuestro orco, que a su vez hereda de la clase enemigo y usamos para ello el inicializador de conveniencia que nos permite dar los valores de fuerza y vida por separado. Si ahora preguntamos por su valor de valia, con orcoFeo.valia, nos dirá que es 300.

Como vemos, la extensión de valia para la clase Enemigo ha sido heredada igualmente por el Orco (que es su hijo) y cualquier Orco que creemos desde ese momento tendrá esta extensión, así como cualquier nuevo enemigo que queramos crear, siempre que sea hijo o subclase de Enemigo.

Repasando

Hemos visto como en esta nueva lección hemos dado una noción básica de las clases y las estructuras (o structs), como extender la funcionalidad de una clase de una manera sencilla y las formas en que la herencia de clases funciona.

Próximamente veremos qué es y cómo funciona el polimorfismo, la sobreescritura de clases o cómo funcionan los controles de acceso de Swift y podemos acceder a los métodos y propiedades de otras clases definidas en otros ficheros desde cualquiera nuevo.

Recordar practicar mucho, hacer vuestras propias modificaciones y usar los Playground de los que ya os hemos dado una guía de trabajo. Y recordad, Good Apple Coding.

Compra "Aprendiendo Swift 3"

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Lecciones Prototipos (I): UITableView

Lecciones por prototipos (I): Vistas de tabla (UITableView)

Primera lección por prototipos, un nuevo e innovador contenido. A veces, lo normal es que nos perdamos sin terminar de entender qué es o cómo funcionan los componentes que forman parte de una app o un juego. Para este caso hemos creado las lecciones por prototipos. Una exploración básica de conceptos esenciales a través de prototipos en Playground que podemos probar con Swift Playgrounds en el iPad o con Xcode 8. En esta primera lección abordamos las UITableView (vistas de tabla). Un elemento esencial en la mayoría de apps de iOS que muchas veces no es entendido desde su base y por lo tanto, provoca un mal uso de las mismas.

  • Hola,
    Me han resultado muy interesantes estas 3 lecciones del curso, vais a publicar mas entradas en un futuro?

    Saludos

    • Julio César Fernández

      Hola Kepa,

      Muchas gracias. Nos alegra que te haya sido útil. El curso de Swift volverá en breve con nuevos e interesantes contenidos.

      Un saludo y cualquier cosa, ya sabes dónde estamos.

  • Pingback: Swift Lección 4: Funciones | Apple Coding()

  • Diego G.

    En el pedazo de codigo de “extension Enemigo”, cuando defines “valia” por qué lo haces de ese modo y no con un “=” seguido de la operación (en este caso fuerza*vida).

    • Julio César Fernández

      Ese modo es a través de variables calculadas, donde defines un valor en base a una función. Podría haber usado también:
      var valia:Int = self.vital.fuerza * self.vital.vida
      Preferí usar una variable calculada para que se viera un ejemplo simple de como una variable puede tener funciones que determinen sus valores.
      Uno u otro, la solución es valida así que si preferiste la otra, genial. Que saquéis vuestras conclusiones y veáis otras maneras es la mejor forma de resolver un problema y aprender.

  • Pingback: Curso de Swift Lección 6, Enumeraciones | Apple Coding()

  • .[Roberto Hevens]

    Julio:

    Con esta lección me has dejado de cama. Muy bien igualmente. Ahora a practicar.

    Un saludo.

  • Pingback: Swift Lección 7, Structs o estructuras | Apple Coding()