diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

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 ou Class
  • 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étodo init podería escribirse na clase como vemos na liñ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 o get (liña 6) e o set (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 defecto newValue para o próximo novo valor.
  • O observador didSet chámase despois de que se actualice o valor de totalPasos. 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).

Share Your Valuable Opinions