Swift tiene otra forma de construir tipos de datos complejos llamada clases. Estas son similares a las estructuras, pero tienen diferencias importantes, incluyendo:
Estas tres son las grandes diferencias, así que las trataré antes de continuar.
Si queremos convertir nuestra estructura Person
en una clase Person
, Swift no nos dejará escribir esto:
class Person {
var clothes: String
var shoes: String
}
Esto es debido a que estamos declarando dos atributos para ser String
, que si recuerdas deben tener un valor obligatoriamente. Esto funcionaba en la estructura porque Swift de forma automática genera un constructor para nosotros que nos obligaba a asignar un valor a ambos, pero esto no ocurre con las clases, luego Swift no puede asegurarse de que vayamos a dar los valores.
Estas son tres posibles soluciones: hacer los dos valores opcionales, darlos valores predeterminados, o crear nuestro propio constructor. La primera opción es "fea" ya que introduce opcionales en todo el código que no es necesario que estén. La segunda opción funciona, pero es poco eficiente a no ser que utilizases los valores predeterminados. Por tanto, la opción adecuada es la tercera: crear tu propio constructor.
Para hacer esto, crea un método dentro de la clase llamado init()
que tome los dos parámetros por los que nos estamos preocupando:
class Person {
var clothes: String
var shoes: String
init(clothes: String, shoes: String) {
self.clothes = clothes
self.shoes = shoes
}
}
Hay dos cosas que podrían llamarte la atención en este código.
Primero, no tienes que escribir func
antes del método init()
ya que este es especial. Segundo, debido a que los nombres de los parámetros pasados son los mismos que los nombres de los atributos que queremos asignar, debemos utilizar self.
para aclararlo - "la propiedad clothes
de este objeto debe ser asignada con el valor del parámetro clothes
que recibe". Puedes darles nombres únicos si quieres, es tu decisión.
Importante: Swift requiere que todos los atributos no opcionales tengan un valor al final del constructor, o antes de la llamada a cualquier otro método - lo que primero ocurra.
La segunda diferencia entre clases y estructuras es que las clases pueden construirse basandose en otras para generar grandes cosas, conocido como herencia de clases. Esta es una técnica muy extendida en Cocoa Touch, incluso en programas básicos, por lo que es algo a lo que debes enfrentarte.
Comencemos con algo simple: una clase Singer
que tiene atributos, los cuales son nombre y edad. Como métodos, tendrá un simple constructor para inicializar los atributos, más un método sing()
que imprime algunas palabras:
class Singer {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func sing() {
print("La la la la")
}
}
Ahora podemos crear una instancia de esa clase llamando al constructor, luego leemos sus propiedades y llamamos al método:
var taylor = Singer(name: "Taylor", age: 25)
taylor.name
taylor.age
taylor.sing()
Esa es nuestra clase básica, pero vamos a desarrollarla: quiero definir una clase CountrySinger
que tenga todo lo que la clase Singer
tiene, pero que cuando llame a sing()
en ella quiero que imprima "Trucks, guitars, and liquos" en su lugar.
Por supuesto puedes copiar y pegar la original Singer
en una nueva clase llamada ContrySinger
pero no es la mejor opción y puede volverse caótico si haces cambios después en la clase Singer
y olvidas copiarlos. En cambio, Swift tiene una solución más inteligente: podemos definir CountrySinger
como basada en Singer
y todos sus atributos y métodos serán obtenidos en la nueva clase:
class CountrySinger: Singer {
}
Los dos puntos son los que hacen la magia: significan que "CountrySinger
extiende Singer
". Ahora, esa nueva clase CountrySinger
(llamada subclase) no añade nada por ahora a Singer
(llamada clase padre, o superclase). Queremos que tenga su propio método sing()
, pero en Swift necesitas aprender una nueva palabra clave: override
. Significa "se que tienes este método ya implementado en mi clase padre, pero quiero cambiarlo en la subclase".
Tener la palabra override
es muy útil, porque deja clara la intención que tienes. También permite a Swift comprobar tu código: si no usas override
, Swift no te permitirá cambiar el método que obtienes de tu superclase, o si usas override
y no hay nada para sobreescribir, Swift te señalará el error.
Luego, tenemos que utilizar override func
, así:
class CountrySinger: Singer {
override func sing() {
print("Trucks, guitars, and liquor")
}
}
Ahora modifiquemos la forma en la que el objeto taylor
es creado:
var taylor = CountrySinger(name: "Taylor", age: 25)
taylor.sing()
Si cambias CountrySinger
a solo Singer
verás que aparecen diferentes mensajes en el panel de resultados.
Ahora, para hacer las cosas más complicadas, vamos a definir una nueva clase llamada HeavyMetalSinger
. Pero esta vez vamos a almacenar una nueva propiedad llamada noiseLevel
que define como de alto le gusta al cantante heavy cantar a su micrófono.
Esto genera un problema, y es necesario resolverlo de una forma particular:
Singer
no tiene un atributo noiseLevel
.HeavyMetalSinger
que acepte el nivel de ruido.name
y age
del cantante de heavy metal, por lo que podemos pasarlo a la superclase Singer
.noiseLevel
) y luego pasar los otros parámetos a la superclase.Puede sonar complicado, pero el código es muy sencillo. Aquí está la clase HeavyMetalSinger
, completada con nuestro propio método sing()
:
class HeavyMetalSinger: Singer {
var noiseLevel: Int
init(name: String, age: Int, noiseLevel: Int) {
self.noiseLevel = noiseLevel
super.init(name: name, age: age)
}
override func sing() {
print("Grrrrr rargh rargh rarrrrgh!")
}
}
Observa cómo el constructor toma tres parámetros, luego llama a super.init()
para pasar name
y age
a la superclase Singer
, pero solo después de asignar valor a nuestro propio atributo. Verás que super
se utiliza mucho cuando se trabaja con clases, y solamente significa "llama a un método de la clase de la que heredo". Normalmente se utiliza para significar "deja que mi clase padre haga todo lo que necesite y después ya haré lo que considere adicional".
La herencia de clases es un gran tema, por lo que no te preocupes si no lo tienes claro aún. Sin embargo, hay una cosa que debes conocer: la herencia de clase a menudo tiene muchos niveles. Por ejemplo, A puede heredar de B, y B puede heredar de C, y C puede heredar de D, y así sucesivamente. Esto permite construir una funcionalidad y reutilizarla en varias clases, ayudando a tener un código modular y fácil de enterder.
Si quieres que parte del sistema operativo de Apple llame al método de tu clase de Swift, debes marcarlo con un atributo especial: @objc
. Es la abriviatura de "Objective-C", y el atributo marca de forma efectiva como disponible para ejecutarse con código del antiguo Objective-C, que es casi todo de iOS, macOS, watchOS, y tvOS. Por ejemplo, si pides al sistema que llame a tu método después de que haya transcurrido un segundo, deberás marcarlo con @objc
.
Por ahora no te preocupes mucho sobre @objc
, no solo lo explicaré en el contexto más adelante, sino que Xcode siempre te dirá cuando es necesario. Alternativamente, si no quieres usar @objc
para métodos individuales, puedes poner @objcMembers
antes de tu clase para automáticamente hacer todos tus métodos disponibles para Objective-C.
Cuando copias una estructura, todo se duplica, incluyendo todos los valores. Esto significa que cambiar una copia de una estructura no cambia el resto, son todas individuales. Con las clases, cada copia de un objeto apunta al objeto original, por lo que si cambiar uno, el resto también cambiará. Swift llama a las estructuras "tipos de valor" ya que tan solo apuntan a un valor, y a las clases "tipos de referencia" ya que todos los objetos son únicamente referencias compartidas al valor real.
Esta es una importante diferencia, y significa que elegir entre estructuras y clases es importante:
Si tuviera que resumir la diferencia clave entre estructuras y clases, diría esto: las clases son más flexibles, mientras que las estructuras son más seguras. Como regla básica, siempre debes utilizar estructuras hasta que tengas una razón específica para usar clases.
SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until February 9th.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.