Las estructuras y las clases son dos de los bloques fundamentales de la programación en Swift. Ambas son formas de agrupar datos y funciones relacionados en unidades de funcionalidad autocontenidas. Y cuando defines una estructura o una clase, estás definiendo un tipo nuevo que se usará en Swift, tal como usaste los tipos con los que ya trabajaste, como Int y String.
Hay muchas similitudes entre las estructuras y las clases en Swift. Entre otras, ambas pueden almacenar valores en propiedades y ofrecer funcionalidad mediante el uso de métodos.
Cada una ofrece funcionalidad adicional, que queda fuera del alcance de este ejercicio.
Tanto las estructuras como las clases se definen más o menos de la misma manera. Empiezan con la palabra clave correspondiente, seguida del nombre del tipo que se está definiendo; después viene el cuerpo de la estructura o la clase, entre llaves.
El cuerpo puede estar formado por propiedades almacenadas, que se definen y se comportan igual que una constante o una variable normales.
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id = makeRandomID()
}
Como se mencionó antes, definir una estructura o una clase es solo definir un tipo nuevo. Es solo el plano de cómo se verán los valores de ese tipo, pero en realidad no crea ningún valor de ese tipo para que trabajes con él.
Para crear una instancia de ese tipo, debes escribir el nombre del tipo seguido de un par de paréntesis.
let someStats = CharacterStats()
let someCharacter = GameCharacter()
Esto creará valores de estos tipos, con las propiedades inicializadas con los valores predeterminados que se indican en la definición. Ten en cuenta que, en casos opcionales como la propiedad name de GameCharacter, si no se proporciona un valor, la propiedad toma el valor nil de forma predeterminada, igual que cuando defines tipos opcionales normales en los que no se proporciona un valor de inmediato.
Con las estructuras, Swift proporciona automáticamente algo llamado inicializador por miembros, en el que puedes indicar valores para las propiedades de la estructura dentro de los paréntesis, lo que sobrescribe los valores predeterminados de la definición.
let differentStats = CharacterStats(health: 100.0, speed: 6, strength: 18)
Se puede acceder a las propiedades de estructuras y clases usando la notación de punto, en la que el nombre del valor va seguido de un punto (.) y del nombre de la propiedad. Si una propiedad de una estructura o de una clase tiene propiedades propias, también puedes usar esta notación de punto para acceder a esas propiedades anidadas.
Puedes usar esta notación tanto para obtener el valor de la propiedad como, cuando esté permitido, para cambiarlo.
someStats.health
// => 0
someCharacter.name
// => nil
someStats.health = 87.3
someStats.health
// => 87.3
someCharacter.name = "Luther"
someCharacter.name
// => "Luther"
someCharacter.id = "new id"
// Error: Cannot assign to property: 'id' is a 'let' constant
Así como las propiedades almacenan datos en tus estructuras y clases, también puedes definir métodos, que almacenan funciones en tu estructura o clase.
Los métodos se definen igual que una función normal, solo que dentro del cuerpo de la estructura o la clase. Ten en cuenta que, si una función cambia el valor de una propiedad de una estructura, debe ir precedida de la palabra clave mutating. Además, si un método puede cambiar una propiedad, esa propiedad debe definirse con var en lugar de let, igual que las variables normales.
struct CharacterStats {
var health = 0.0
var speed = 0
var strength = 0
mutating func takeHit(_ damage: Double) {
health = max(0.0, health - damage)
}
func canLift(_ weight: Int) -> Bool {
weight < strength * 100
}
}
class GameCharacter {
var stats = CharacterStats()
var characterClass: String?
var name: String?
var active = false
let id: String = makeRandomID()
func takesDamage(_ damage: Double) {
stats.takeHit(damage)
if stats.health <= 0 {
active = false
}
}
func sayName() -> String {
return "My name is \(name ?? "no one"), my class is \(characterClass ?? "undetermined")"
}
func lift(_ weight: Int) -> String {
if stats.canLift(weight) {
return "No problem!"
} else {
return "Ooof! No way."
}
}
}
Puedes llamar a estos métodos usando la notación de punto, igual que con las propiedades.
var myChar = GameCharacter()
myChar.stats = CharacterStats(health: 72.8, speed: 19, strength: 6)
myChar.active = true
myChar.lift(750)
// => "Ooof! No way."
myChar.takesDamage(80)
myChar.active
// => false
Cada instancia de una estructura o una clase tiene una propiedad implícita llamada self, que hace referencia a la instancia misma.
El uso más común de self es distinguir entre una propiedad de la instancia y un parámetro que tiene el mismo nombre:
struct Position {
var x = 0
mutating func move(x: Int) {
// Here, 'self.x' refers to the struct's property,
// while 'x' refers to the parameter passed to the method.
self.x = x
}
}
Los tipos de Swift también ofrecen una palabra clave Self con mayúscula inicial, que hace referencia al tipo de la instancia en lugar de a la instancia misma.
En este ejercicio, vas a simular un sistema de computadora basado en ventanas. Vas a crear algunas ventanas que se pueden mover y redimensionar, y que muestran su contenido. La siguiente imagen es representativa de los valores con los que vas a trabajar a continuación.
<--------------------- screenSize.width --------------------->
^ ╔════════════════════════════════════════════════════════════╗
| ║ ║
| ║ position.x,_ ║
| ║ position.y \ ║
| ║ \<----- size.width -----> ║
| ║ ^ *──────────────────────┐ ║
| ║ | │ title │ ║
| ║ | ├──────────────────────┤ ║
screenSize.height ║ | │ │ ║
| ║ size.height │ │ ║
| ║ | │ contents │ ║
| ║ | │ │ ║
| ║ | │ │ ║
| ║ v └──────────────────────┘ ║
| ║ ║
| ║ ║
v ╚════════════════════════════════════════════════════════════╝
Define una estructura llamada Size con dos propiedades Int, width y height, que almacenan el ancho y la altura actuales de la ventana, respectivamente.
El ancho y la altura iniciales deben ser 80 y 60, respectivamente.
Incluye un método resize(newWidth:newHeight:) que recibe nuevos parámetros de ancho y altura y cambia las propiedades para reflejar el nuevo tamaño.
let size1080x764 = Size(width: 1080, height: 764)
// Returns Size
var size1200x800 = size1080x764
// Returns Size
size1200x800.resize(newWidth: 1200, newHeight: 800)
size1200x800.height
// Returns 800
Define una estructura llamada Position con dos propiedades Int, x y y, que almacenan la posición horizontal y vertical actual, respectivamente, de la esquina superior izquierda de la ventana.
Los valores iniciales de x e y deben ser 0 en ambos casos. La posición (0, 0) es la esquina superior izquierda de la pantalla, con valores de x que aumentan a medida que te mueves a la derecha y valores de y que aumentan a medida que te mueves hacia abajo.
Incluye un método moveTo(newX:newY:) que recibe nuevos parámetros x e y y cambia las propiedades para reflejar la nueva posición.
var point = Position(x: 10, y: 20)
// Returns Position
point.moveTo(newX: 100, newY: -100)
point.y
// Returns -100
Define una clase de ventana con las siguientes propiedades:
title : String, el valor inicial es «New Window»screenSize : Size, valor constante con width = 800 y height = 600size : Size, el valor inicial es el valor predeterminado de la estructura Size
position : Position, el valor inicial es el valor predeterminado de la estructura Position
contents : String?, el valor inicial es nil
También debes definir un inicializador vacío para la clase.
let window = Window()
window.title
// Returns "New Window"
resize(to:) : (Size) -> () - Este método recibe una estructura Size como argumento e intenta redimensionar la ventana al tamaño especificado. Sin embargo, el nuevo tamaño no puede exceder ciertos límites.x = 400, y = 300 y se solicita un redimensionado a height = 400, width = 300, entonces la ventana se redimensionará a height = 300, width = 300, ya que la pantalla no es lo suficientemente grande en la dirección y para satisfacer completamente la solicitud.move(to:) : (Position) -> () - Esto es similar a resize(to:); sin embargo, este método ajusta la posición de la ventana al valor solicitado, en lugar del tamaño. Al igual que con resize, la nueva posición no puede exceder ciertos límites.x como para y.x = 250, y = 100 y se solicita un movimiento a x = 600, y = 200, entonces la ventana se moverá a x = 550, y = 200, ya que la pantalla no es lo suficientemente grande en la dirección x para satisfacer completamente la solicitud.update(title:) : (String) -> () - Este método establece la propiedad title al valor del string que se pasó.update(text:) : (String?) -> () - Este método establece la propiedad contents al valor del string opcional que se pasó.display() : () -> String - Este método devuelve un string que describe el estado actual de la ventana. Por ejemplo, si la ventana tiene el title «My First Window» con posición: x = 10, y = 100; tamaño: width = 200, height = 150; y contenido: «I 😍 my window», debe devolver el string: "My First Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n" - Si contents es nil, la última línea debe decir «[This window intentionally left blank]»El sistema de ventanas debe tener un inicializador para que la ventana se pueda crear con argumentos personalizados.
Crea otro inicializador para la clase Window.
El inicializador debe recibir los siguientes parámetros: title, contents y dos parámetros opcionales size y position.
Los parámetros size y position deben tener como valores predeterminados los valores predeterminados de las estructuras Size y Position.
let window = Window(title: "My First Window", contents: "I 😍 my window")
window.display()
// Returns "My First Window\nPosition: (0, 0), Size: (80 x 60)\nI 😍 my window\n"
let window2 = Window(title: "My Second Window", contents: "I 😍 my window", size: Size(width: 200, height: 150), position: Position(x: 10, y: 100))
window2.display()
// Returns "My Second Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n"
Regístrate en Exercism para aprender y dominar Swift con 35 conceptos116 ejercicios y mentoría humana real, todo gratis.