
Swift – Métodos de Instancia y de Tipo
En este nuevo Tutorial Swift aprenderemos acerca de los métodos de instancia y de tipo. Los métodos son funciones que están asociadas con un tipo en particular. Las clases, estructuras y enumeraciones, todas, pueden definir métodos de instancia, que encapsulan tareas y funciones específicas para trabajar con una instancia de un tipo dado. Las clases, estructuras y enumeraciones también pueden definir métodos de tipo, que se asocian con el tipo en sí.
[anuncio_b30 id=1]
Se que no suena del todo claro lo antes dicho, así que permítanme intentar desarrollar mejor estas ideas a base de ejemplos.
Métodos de instancia
Los métodos de instancia son funciones que pertenecen a instancias de una clase, estructura o enumeración, estas aportan la funcionalidad de las instancias, ya sea proporcionando formas de acceder y modificar las propiedades, o proporcionando funcionalidad relacionada con el propósito de la instancia. Ejemplo:
class Counter { var count = 0 func increment() { ++count } // increment func incrementBy(amount: Int) { count += amount } // incrementBy func reset() { count = 0 } // reset } // Counter
…en este código hemos creado la clase Counter, tres métodos de instancia y una propiedad. Más allá de que se les llama métodos de instancia en lugar de funciones, nada de esto es nuevo, ya lo hemos visto en otros artículos de este Curso Swift.
La propiedad self
Cada instancia cuenta con una propiedad implícita llamada self, que es exactamente equivalente a la propia instancia. Utilizamos la propiedad self para hacer referencia a la instancia actual dentro de sus propios métodos de instancia, self vendría a ser como el this de C++ o Java.
El método increment() en el ejemplo anterior se podría haber escrito así:
func increment() { self.count++ } // increment
…escribir self en este caso no es necesario ya que el compilador asume que se está haciendo referencia a una propiedad de la clase donde se encuentra este método de instancia.
Modificación de tipos por valores
Las estructuras, las enumeraciones y las tuplas son tipos por valor o Value Types en ingles. Sin entrar en muchos detalles (este tema lo tocaremos en otro Curso Swift) digamos que esto significa que almacenan una copia única de los datos que engloban y como es de esperar estas características establecen ciertas particularidades en estos tipos. Veamos un ejemplo de esto:
//: Playground - noun: a place where people can play import UIKit class Crane { var craneName: String = "None" } // Crane struct DataStruct { var someNumber: Int = 0 } // DataStruct enum BoomLevel { case High case Half case Low } // BoomLevel //-------------------------------------------------------------------------------------- var MyCrane: Crane = Crane() MyCrane.craneName = "Crane - Backyard" print("The name of the crane is: (MyCrane.craneName)") //-------------------------------------------------------------------------------------- var MyStruct: DataStruct = DataStruct() MyStruct.someNumber = 10 print("The property value of the structure is: (MyStruct.someNumber)") //-------------------------------------------------------------------------------------- var MyEnum: BoomLevel MyEnum = BoomLevel.High print("The value of MyEnum is: (MyEnum)") MyEnum = BoomLevel.Half print("The value of MyEnum now is: (MyEnum)")
…la salida en pantalla sería:
The name of the crane is: Crane - Backyard The property value of the structure is: 10 The value of MyEnum is: High The value of MyEnum now is: Half
De las línea 5 a la 23 tenemos la declaración de la clase Crane, la estructura DataStruct y la enumeración BoomLevel. Luego de la línea 27 a la 51 hacemos uso de estas, creamos instancias de las mismas, las inicializamos y le asignamos un valor, terminamos por imprimir en pantalla los valores añadidos.
[anuncio_b30 id=2]
Hasta aquí todo bien, las tres instancias fueron declaradas como variables, pero ¿qué sucedería si tuviéramos métodos de instancia y estos modificaran el valor de las instancias a las que pertenecen? Aquí algunos cambios:
//: Playground - noun: a place where people can play import UIKit class Crane { var craneName: String = "None" func changeName(newName: String) { craneName = newName } // changeName } // Crane struct DataStruct { var someNumber: Int = 0 func addingOne() { someNumber++ } // changeProperty } // DataStruct enum BoomLevel { case High case Half case Low func changeLevel(newValue: BoomLevel) { switch newValue { case High: self = Half case Half: self = Low case Low: self = Half } // switch } // changeProperty } // BoomLevel //-------------------------------------------------------------------------------------- var MyCrane: Crane = Crane() MyCrane.craneName = "Crane - Backyard" print("The name of the crane is: (MyCrane.craneName)") MyCrane.changeName("Crane - Buildings") print("The name of the crane is: (MyCrane.craneName)") //-------------------------------------------------------------------------------------- print("") var MyStruct: DataStruct = DataStruct() MyStruct.someNumber = 10 print("The property value of the structure is: (MyStruct.someNumber)") MyStruct.addingOne() print("The property value of the structure is: (MyStruct.someNumber)") //-------------------------------------------------------------------------------------- print("") var MyEnum: BoomLevel MyEnum = BoomLevel.High print("The value of MyEnum is: (MyEnum)") MyEnum.changeLevel(BoomLevel.Half) print("The value of MyEnum now is: (MyEnum)")
…la salida en pantalla sería:
Playground execution failed: /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:23:19: error: cannot pass immutable value to mutating operator: 'self' is immutable someNumber++ ~~~~~~~~~~^ /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:21:5: note: mark method 'mutating' to make 'self' mutable func addingOne() { ^ mutating /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:41:18: error: cannot assign to value: 'self' is immutable self = Half ~~~~ ^ /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:35:5: note: mark method 'mutating' to make 'self' mutable func changeLevel(newValue: BoomLevel) { ^ mutating /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:45:18: error: cannot assign to value: 'self' is immutable self = Low ~~~~ ^ /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:35:5: note: mark method 'mutating' to make 'self' mutable func changeLevel(newValue: BoomLevel) { ^ mutating /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:49:18: error: cannot assign to value: 'self' is immutable self = Half ~~~~ ^ /var/folders/36/gv16k48s4wn_92bbl72jj5f80000gn/T/./lldb/48513/playground385.swift:35:5: note: mark method 'mutating' to make 'self' mutable func changeLevel(newValue: BoomLevel) { ^ mutating
…exactamente, hay errores en el código debido a una características propia del lenguaje como lo es el hecho de que ningún método de instancia de una estructura o enumeración puede modificar una propiedad miembro de las mismas, algo que si podemos lograr en una clase. Sin embargo tampoco es imposible de lograr, para ello necesitamos hacer uso de la palabra clave mutating en conjunto a la declaración de cada método de instancia.
[anuncio_b30 id=3]
El código anterior ya corregido quedaría de la siguiente forma:
//: Playground - noun: a place where people can play import UIKit class Crane { var craneName: String = "None" func changeName(newName: String) { craneName = newName } // changeName } // Crane struct DataStruct { var someNumber: Int = 0 mutating func addingOne() { someNumber++ } // addingOne } // DataStruct enum BoomLevel { case High case Half case Low mutating func changeLevel(newValue: BoomLevel) { switch newValue { case High: self = Half case Half: self = Low case Low: self = Half } // switch } // changeProperty } // BoomLevel //-------------------------------------------------------------------------------------- var MyCrane: Crane = Crane() MyCrane.craneName = "Crane - Backyard" print("The name of the crane is: (MyCrane.craneName)") MyCrane.changeName("Crane - Buildings") print("The name of the crane is: (MyCrane.craneName)") //-------------------------------------------------------------------------------------- print("") var MyStruct: DataStruct = DataStruct() MyStruct.someNumber = 10 print("The property value of the structure is: (MyStruct.someNumber)") MyStruct.addingOne() print("The property value of the structure is: (MyStruct.someNumber)") //-------------------------------------------------------------------------------------- print("") var MyEnum: BoomLevel MyEnum = BoomLevel.High print("The value of MyEnum is: (MyEnum)") MyEnum.changeLevel(BoomLevel.Half) print("The value of MyEnum now is: (MyEnum)")
The name of the crane is: Crane - Backyard The name of the crane is: Crane - Buildings The property value of the structure is: 10 The property value of the structure is: 11 The value of MyEnum is: High The value of MyEnum now is: Low
…problema resuelto.
Métodos de tipo
Los métodos de instancia, como se describió anteriormente, son métodos que se llaman en una instancia de un tipo particular. Pues también podemos definir métodos de tipo que es como se les llama en Switch a los métodos estáticos de toda la vida. Al igual que en otros lenguajes de programación estos se declaran escribiendo la palabra clave static antes de la palabra clave func.
Un ejemplo podría ser:
class SomeClass { static func someTypeMethod() { print("Método de tipo o método estático!!!") } // someTypeMethod } // SomeClass SomeClass.someTypeMethod()
…esto es válido tanto para las clases como para las estructuras y las enumeraciones.
Terminemos este tema con el siguiente ejemplo, donde veremos varios de los tópicos tocados en este artículo:
//: Playground - noun: a place where people can play import UIKit struct LevelTracker { static var highestUnlockedLevel = 1 static func unlockLevel(level: Int) { if level > highestUnlockedLevel { highestUnlockedLevel = level } // if } // unlockLevel static func levelIsUnlocked(level: Int) -> Bool { return level <= highestUnlockedLevel } // levelIsUnlocked var currentLevel = 1 mutating func advanceToLevel(level: Int) -> Bool { if LevelTracker.levelIsUnlocked(level) { currentLevel = level return true } else { return false } // else } // advanceToLevel } // LevelTracker class Player { var tracker = LevelTracker() let playerName: String init(name: String) { playerName = name } // init func completedLevel(level: Int) { LevelTracker.unlockLevel(level + 1) tracker.advanceToLevel(level + 1) } // completedLevel } // Player var player = Player(name: "Bobby") player.completedLevel(1) print("The highest unlocked level by player (player.playerName) is now (LevelTracker.highestUnlockedLevel)")
…la salida en pantalla sería:
The highest unlocked level by player Bobby is now 2
Cualquier duda con este ejemplo, dejen sus preguntas en los comentarios!
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!