As propiedades en Swift son variables ou constantes que creamos dentro dos tipos (dentro dunha Struct
ou Class
).
- Son os datos encapsulados dentro dunha
Struct
ouClass
- Son moi útiles para poder gardar valores e acceder a eles durante o transcurso da execución da nosa aplicación.
No seguinte exemplo, creamos unha clase ContaBancaria
. Será necesario almacenar algúns datos, como:
- Un número de conta bancaria
- O saldo que se mantén actualmente na conta.
As propiedades decláranse da mesma maneira que calquera outra variable e constante:
class ContaBancaria {
var numConta: Int = 0
var saldo: Float = 0.0
}
IMPORTANTE #
Nesta sección vamos a empregar case sempre clases, pero tede en conta que todo o que veremos se emprega exactamente igual no caso das estruturas.
Obxectos ou instancias de clase ou de estrutura #
Ata o de agora, todo o que fixemos é definir o plan para a nosa clase ou estrutura. Para facer algo con esta clase, necesitamos crear instancias delas.
O primeiro paso neste proceso é declarar unha variable para almacenar unha referencia á instancia cando se crea. Facemos isto dna liña 15
:
class ContaBancaria {
var numConta: Int = 0
var saldoConta: Float = 0.0
func visualizarSaldo(){
print("O número de conta é: \(numConta)")
print("Saldo actual: \(saldoConta)")
}
func getSaldoMax() -> Float{
return 10000000.00
}
}
var conta: ContaBancaria = ContaBancaria()
Cando se execute a liña 15
, crearase unha instancia da clase ContaBancaria
e poderase acceder a ela a través da variable conta
.
Inicializar obxectos/instancias #
Unha clase a miúdo terá que realizar algunhas tarefas de inicialización.
- Estas tarefas pódense implementar colocando un método de
init
dentro da clase/estrutura. - No caso da clase
ContaBancaria
, sería útil poder inicializar o número de conta e saldo cando se crea unha nova instancia de clase (obxecto). Para lograr isto, o métodoinit
podería escribirse na clase como vemos naliña 5
. - Ao crear unha instancia da clase (nunha estrutura sería igual), agora será necesario proporcionar valores de inicialización para o número de conta e as propiedades de saldo (
liña 20
)
class ContaBancaria {
var numConta: Int = 0
var saldoConta: Float = 0.0
init(numero: Int,saldo: Float) {
numConta = numero
saldoConta = saldo
}
func visualizarSaldo(){
print("O número de conta é: \(numConta)")
print("Saldo actual: \(saldoConta)")
}
func getSaldoMax() -> Float{
return 10000000.00
}
}
var conta1 = ContaBancaria(numero: 1234, saldo: 0.00)
Limpeza de obxectos/instancia de clase #
Tal e como vimos na seccion anterior, esta característica non está dispoñible en estruturas.
Calquera tarefa de limpeza que deba realizarse antes de que unha instancia de clase sexa destruída polo sistema de tempo de execución de Swift pódese realizar implementando deinit
dentro da definición de clase:
class ContaBancaria {
var numConta: Int = 0
var saldoConta: Float = 0.0
init(numero: Int,saldo: Float) {
numConta = numero
saldoConta = saldo
}
deinit {
//Código que se execute unha vez que se destrúa a clase
}
func visualizarSaldo(){
print("O número de conta é: \(numConta)")
print("Saldo actual: \(saldoConta)")
}
func getSaldoMax() -> Float{
return 10000000.00
}
}
Propiedade calculada #
Unha propiedade calculada é un valor que se establece a través dun cálculo determinado. Impleméntanse creando métodos get
e set
opcionais que conteñen o código para realizar o cálculo.
Así, por exemplo, a clase ContaBancaria
podería necesitar unha propiedade adicional para conter o saldo actual menos calquera tarifa bancaria recente (saldoMenosTarifa
)
- Na propiedade anterior engadimos un
get
(liña 8
) que devolve unha propiedade calculada baseada no saldo actual menos a cantidade dunha tarifa. - Tamén temos un
set
(liña 11
) para establecer o valor do saldo menos as tarifas. - Aínda que se trata de propiedades calculadas, accédese a elas da mesma maneira que ás propiedades almacenadas utilizando a anotación de puntos (
liña 17
)
class ContaBancaria {
var numConta: Int = 0
var saldoConta: Float = 0.0
var tarifa: Float = 25.0
var saldoMenosTarifa: Float {
get{
return saldoConta - tarifa
}
set(novoSaldo) {
saldoConta = novoSaldo - tarifa
}
}
}
print("Saldo: \(conta1.saldoMenosTarifa)")
Propiedade calculada de so lectura #
Unha propiedade calculada con get
pero sen set
coñécese como unha propiedade calculada de só lectura.
- Unha propiedade calculada de só lectura sempre devolve un valor, e pódese acceder a ela a través da sintaxe de puntos, pero non se pode establecer nun valor diferente.
- Deben declararse como variables, con var, nunca como constantes (
let
)
NOTA: Podemos simplificar a declaración dunha propiedade calculada de só lectura eliminando a palabra chave get e as chaves ({ }
):
class Cubo {
var ancho = 0.0, alto = 0.0, profundidade = 0.0
var volume: Double {
return ancho * alto * profundidade
}
init (ancho: Double, alto: Double, profundidade: Double) {
self.ancho = ancho
self.alto = alto
self.profundidade = profundidade
}
}
var miCubo = Cubo(ancho: 4.0, alto: 5.0, profundidade: 2.0)
print("O volume de miCubo é: \(miCubo.volume) (4*5*2)")
//Imprime: O volume de miCubo é: 40.0 (4*5*2)
newValue – oldValue #
En determinados métodos, observadores (vense un pouco máis adiante), funcións… é necesario acceder aos novos ou anteriores valores dunha propiedade.
- Mediante
newValue
accedemos ao valor novo dunha propiedade - Mediante
oldValue
ao anterior.
class ContadorPasos {
var totalPasos: Int = 0 {
willSet {
print("Asignación de número de pasos: \(newValue)")
}
didSet {
if totalPasos > oldValue {
print("\(totalPasos - oldValue) pasos engadidos")
}
}
}
}
@propertyWrapper #
Con frecuencia unha propiedade calculada é común a múltiples clases ou estruturas.
- Antes da versión 5.1 de Swift, a única forma de compartir a propiedade calculada era duplicar o código e incrustar a propiedade en todas as clases que a necesitasen
- Isto é moi ineficiente
- Ademáis, un cambio no comportamento no cálculo da propiedade debe propagarse manualmente a todas as clases que a utilizan.
Para abordar esta deficiencia, Swift 5.1. introduciu os envoltorios de propiedades ou property wrappers:
Os property wrappers definen unha propiedade calculada que se pode reutilizar en varias clases.
Para que os property wrappers? #
Imaxina unha clase cunha propiedade destinada a conter un nome de cidade, pero este nome ten que estar almacenado en letras maiúsculas, independentemente de como o ingresase o usuario.
Para iso, poderíase agregar unha propiedade calculada:
class Direccion {
private var nomeCidade: String = ""
var cidade: String {
get {nomeCidade}
set {nomeCidade = newValue.uppercased()}
}
}
var direccion = Direccion()
direccion.cidade = "Ourense"
print("A miña cidade en maiúsculas: \(direccion.cidade)")
//Imprime: A miña cidade en maiúsculas: OURENSE
Cando se asigna un nome de cidade á propiedade, o set
dentro da propiedade calculada convérteo en maiúsculas antes de almacenalo na variable privada nomeCidade
.
A propiedade calculada realiza a tarefa de converter a cadea do nome da cidade en maiúsculas, pero:
Se necesitamos o mesmo comportamento noutras clases, o código tería que duplicarse nesas declaracións.
Property Wrapper #
A partir do exemplo anterior, en lugar dunha propiedade calculada, vamos a implementar un property wrapper.
Para iso:
- Decláranse utilizando a directiva
@propertyWrapper
(liña 1
) e impleméntanse nunha clase ou estrutura (liña 2
) - Deben incluír de xeito obrigatorio unha propiedade chamada «
wrappedValue
» (liñas 5 a 8
) que contén oget
(liña 6
) e oset
(liña 7
) - O inicializador
init
(liña 10 a 12
) é opcional
@propertyWrapper
class ConvertirMaiusculas {
private var valor: String = ""
var wrappedValue: String {
get {valor}
set {valor = newValue.uppercased()}
}
init (wrappedValue valorInicial: String){
self.wrappedValue = valorInicial
}
}
class Contacto {
@ConvertirMaiusculas var nome: String
@ConvertirMaiusculas var cidade: String
@ConvertirMaiusculas var pais: String
init(nome: String, cidade: String, pais: String) {
self.nome = nome
self.cidade = cidade
self.pais = pais
}
}
var contacto = Contacto(nome: "Diego Rodicio", cidade: "Ourense", pais: "España")
print("Son \(contacto.nome), de \(contacto.cidade) -\(contacto.pais)-")
//IMPRIME: Son DIEGO RODICIO, de OURENSE -ESPAÑA-
Outro exemplo #
Neste exemplo creamos un @propertyWrapper
para que cada vez que gardemos un nome de usuario, este teña un tamaño maior ca 5. En caso contrario, non se almacena:
@propertyWrapper
struct ValidaciónUsuario {
private var nome: String
init() { self.nome = "" }
var wrappedValue: String {
get { nome }
set {
if newValue.count > 5 {
self.nome = newValue
print("Nome OK!")
} else {
print("Error")
}
}
}
}
struct FormularioUsuario {
@ValidaciónUsuario var nomeUsuario: String
}
var formularioUsuario = FormularioUsuario()
formularioUsuario.nomeUsuario = "drodicio"
print(formularioUsuario.nomeUsuario)
// RESULTADO 👇
// Nome OK!
// drodicio
formularioUsuario.nomeUsuario = "dro"
print(formularioUsuario.nomeUsuario)
// RESULTADO 👇
// Error
// drodicio
observer – Observadores de propiedades #
Os observadores de propiedades observan e responden os cambios no valor dunha propiedade. Chámanse cada vez que se establece o valor dunha propiedade.
Hai dous observadores posibles:
willSet
: chámase xusto antes de que se almacene o valor.didSet
: chámase inmediatamente despois de almacenar o novo valor.
class ContadorPasos {
var totalPasos: Int = 0 {
willSet {
print("Asignación de número de pasos: \(newValue)")
}
didSet {
if totalPasos > oldValue {
print("\(totalPasos - oldValue) pasos engadidos")
}
}
}
}
var pasos = ContadorPasos()
pasos.totalPasos = 200
//Imprime:
//Asignación de número de pasos: 200
//200 pasos engadidos
pasos.totalPasos = 350
//Imprime:
//Asignación de número de pasos: 350
//150 pasos engadidos
A clase ContadorPasos
declara unha propiedade totalPasos que contén os observadores de willSet
e didSet
.
- O observador
willSet
deste exemplo utiliza un nome de parámetro por defectonewValue
para o próximo novo valor. - O observador
didSet
chámase despois de que se actualice o valor detotalPasos
. Compara o novo valor co anterior e, se o número de pasos aumentou, imprímese unha mensaxe para indicar cantos pasos novos se realizaron.
newValue e oldValue #
IMPORTANTE: O observador willSet
non proporciona un nome de parámetro personalizado para o novo valor, no seu lugar utilízase o nome predeterminado de newValue
(liña 4
).
IMPORTANTE: O observador didSet
non proporciona un nome de parámetro personalizado para o valor antigo, no seu lugar utilízase o nome predeterminado de oldValue
(liña 8
).