Propiedades

Contenido

Cuando un tipo de dato declara variables o constantes dentro de su mismo contexto, a éstas se les conocen como propiedades, no como variables.

class User {
	var name: String
	var email: String

	init(name: String, email: String) {
		self.name = name
		self.email = email
	}
}

En el ejemplo anterior, una clase User tiene dos propiedades: name y email, ambas de tipo String. Una propiedad contiene información estructurada relacionada con el tipo de dato que la almacena.

let oscar = User(name: "Oscar Swanros", email: "oscar@swanros.com")

print(oscar.name) // #=> "Oscar Swanros"
print(oscar.email) // #=> "oscar@swanros.com"

Tipos de propiedades

Almacenadas (stored properties)

class Location {
	var lat: Double
	var long: Double
	
	init(lat: Double, long: Double) {
		self.lat = lat
		self.long = long
	}
}

Se le conoce como “propiedad almacenada” (stored property) a aquella propiedad que va a contener un valor. Este es un tipo de propiedades bastante común, y seguramente el que se usará el 90% de las veces que se desee trabajar con propiedades.

Computadas (computed properties)

struct User {
	let firstName: String
	let lastName: String
	
	var fullName: String {
		return "\(firstName) \(lastName)"
	}
}


let oscar = User(firstName: "Oscar", lastName: "Swanros")
print(oscar.fullName) // #=> "Oscar Swanros"

Una propiedad computada no almacena valor alguno. El valor que se obtiene al consultar una propiedad computada es calculado en el momento y no se almacena por la instancia misma. Las propiedades computadas, siempre tienen que ser declaradas como var, no let.

Aunque técnicamente una propiedad computada puede comportarse como una función, no se aconseja que se empleen como funciones.

Inicializando propiedades

Hay una serie de reglas que se deben de cumplir cuando se trabaja con propiedades, ya sea para clases o para estructuras de Swift.

Clases

class Email {
	var from: String
	var to: String
	var subject: String
}

El ejemplo anterior, aunque semánticamente correcto, retornará un error al momento de querer ser compilado, con el mensaje “Class Email has no initializers.”

Una clase debe de tener valores asignados a todas sus propiedades antes de terminar su inicialización. El error del ejemplo previo se puede resolver de dos formas:

// Forma 1: agregando valores por defecto

class Email {
	var from: String = ""
	var to: String = ""
	var subject: String = ""
}

Al agregar valores por defecto a cada una de las propiedades, se cumple la regla. Aunque esta solución es técnicamente válida, puede que no sea la mejor forma de resolver el problema, puesto que no garantiza la integridad de una instancia de Email, ya que se pueden crear instancias de este tipo de dato que no contengan la información necesaria para que la misma sea considerada “válida”.

// Forma 2: declarando un inicializador

class Email {
	var from: String
	var to: String
	var subject: String
		
	init(from: String, to: String, subject: String) {
		self.from = from
		self.to = to
		self.subject = subject
	}
}

Al declarar un inicializador para la clase Email, la integridad de las instancias de esta clase está garantizada puesto que usando esta técnica no puede haber instancias de Email que no contengan los 3 valores necesarios para hacer que un Email sea “válido”.

Propiedades en Subclases

Si se está trabajando con una subclase, la subclase debe de asegurarse de que todas las propiedades de la subclase que no estén en la superclase sean inicializadas antes de inicializar la superclase.

class Animal {
    var family: Family
    var species: String

    init(family: Family, species: String) {
        self.family = family
        self.species = species
    }
}

class Tiger: Animal {
	var age: Int
	
    init(age: Int) {
	    // Inicializar age antes de seguir con la inicialización de la
	    // superclase
		self.age = age 
		
        super.init(family: .felidae, species: "Panthera tigris")
    }
}

Si la superclase cuenta con propiedades computadas, se puede sobreescribir el comportamiento de dichas propiedades computadas usando la palabra reservada override:


class StoreItem {
    var price: Double
    var name: String
    
    var description: String {
        return "\(name) costs $\(price)USD"
    }
    
    init(price: Double, name: String) {
        self.price = price
        self.name = name
    }
}

class Cup: StoreItem {
	// Sobreescribir el comportamiento de la propiedad computada `description`
	// de la superclase StoreItem.
    override var description: String {
        return "\(name) is worth \(price) dollars."
    }
}

let item = StoreItem(price: 12, name: "Cup")
let cup = Cup(price: 12, name: "Cup")

print(item.description) // #=> Cup costs $12.0USD
print(cup.description) 	// #=> Cup is worth 12.0 dollars.

Estructuras

Una estructura sigue reglas bastante parecidas a las clases cuando se trata de manejar propiedades.

struct Book {
    let author: String
    let numberOfPages: Int
    let editorial: String
}

El ejemplo anterior declara una estructura Book sin inicializadores ni valores por defecto para cada una de sus propiedades. Dentro del contexto donde la estructura fue declarada, Swift sintetiza inicializadores con cada uno de sus propiedades automáticamente.

let book = Book(author: "J. R. R. Tolkien", numberOfPages: 580, editorial: "Cool Books Inc.")

Si a alguna de las propiedades de la estructura se le asigna un valor por defecto, esa propiedad será omitida del inicializador sintetizado automáticamente por Swift:

struct Book {
    let author: String
    let numberOfPages: Int
    let editorial: String = "Cool Books Inc."
}

let book = Book(author: "Sam Navajo", numberOfPages: 320)

Cuando todas las propiedades de una estructura tienen valores por default, se omiten todos los parámetros del inicializador.


Relacionados