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.

Ao crear un tipo, xa sexa Class ou Struct, creamos unha abstracción para poder traballar no noso código:

  • Son construcións flexibles de propósito xeral que se converten como se fosen os bloques de construción do código.
  • Definimos propiedades e métodos para agregarlle funcionalidade, utilizando a mesma sintaxe que utilizamos para definir constantes, variables e funcións.

No seguinte exemplo, vou crear unha Struct para crear un tipo Usuario que teña unha serie de propiedades e métodos:

struct Usuario {
    let nome: String
    let cidade: String

    func createUser() {
        print("Creando usuario...")
    }

    func removeUser() {
        print("Borrando usuario")
    }
}

Acabamos de crear o noso primeiro tipo, creamos unha Struct que encapsula os datos que debe almacenar un Usuario (propiedades) e certas operacións que podo facer con eses datos (métodos para crear ou borrar un Usuario).

NOTA:

Que diferenza hai entre un método e unha función? Ningunha, Os métodos son funcións que están asociados a un tipo en particular.

Class vs Struct #

Aínda que unha Class e Struct úsanse con propósitos diferentes, as dúas teñen cousas en común:

  • Crear propiedades para almacenar valores
  • Crear métodos para crear lóxica
  • Crear inicializadores
  • Conformar protocolos para engadir máis funcionalidade

Pero a Class ten funcionalidade adicional que non atopamos na Struct:

  • Podemos usar herdanza só en Class
  • Podemos deinicializar unha Class para liberar recursos
  • Podemos ter a mesma referencia da instancia dunha Class en varias partes do noso código (a Class é reference type e Struct é value type)”

Sintaxe #

Vamos a comparar a sintaxe dunha estrutura cunha clase equivalente:

struct ExemploEstrutura {
    var nome: String
       
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

class ExemploClase {
    var nome: String
       
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

Ademais do uso da palabra chave struct en lugar de class, as dúas declaracións son idénticas.

Instancias #

Para poder usar no noso código os novos tipos ExemploClase e ExemploEstrutura o primeiro que debemos facer é crear unha instancia deles.

En Swift, unha instancia é un obxecto concreto dunha Class ou Struct.

  • Cando creas unha instancia, asígnase espazo en memoria para almacenar os datos desa Class ou Struct
  • Tamén se inicializan cos seus valores iniciais.
  • Ao crear a instancia podes usar as propiedades e métodos do tipo.

Ao crear unha instancia dos nosos tipos é obrigatorio dar un estado inicial a todas as súas propiedades.

  • Para dar un estado inicial adóitase utilizar un inicializador, unha función que recibe valores para asignalos ás propiedades, desta maneira poden ter un valor e pódese crear a instancia dun tipo correctamente.
  • Para crear un inicializador usamos o nome init e pasamos tantos parámetros como propiedades debamos inicializar no noso tipo.

Así, para poder instanciar a Class ou a Struct que acabamos de crear, necesitamos darlle un valor por defecto ás propiedades nome. Para facelo usamos o inicializador, o noso inicializador acepta un parámetro de entrada que se acaba asignando á propiedade nome (liñas 4 e 16):

struct ExemploEstrutura {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

class ExemploClase {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

Agora xa so falta crear as instancias da Class e da Struct, no seguinte exemplo se corresponde coas dúas últimas liñas. A instancia da clase é ‌miClase e a da estructura miEstrutura:

struct ExemploEstrutura {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

class ExemploClase {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

var miEstrutura = ExemploEstrutura(nome: "Lola Mento")
var miClase = ExemploClase(nome: "Aitor Tilla")


Agora, xa podemos acceder aos métodos da clase e da estrutura a través das súas instancias. Velaquí o exemplo completo:

struct ExemploEstrutura {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

class ExemploClase {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

var miEstrutura = ExemploEstrutura(nome: "Lola Mento")
var miClase = ExemploClase(nome: "Aitor Tilla")

print("\(miEstrutura.construirSaudo())")
//Imprime: Ola Lola Mento

print("\(miClase.construirSaudo())")
//Imprime: Ola Aitor Tilla

Paso por valor vs Paso por referencia #

Dada a similitude entre clases e estruturas, é importante comprender como difiren as dúas. Antes de explorar a diferenza máis significativa, primeiro é necesario comprender os conceptos paso por valor e paso por referencia.

A maior diferencia entre as estruturas e as clases está cando as instancias de estrutura e clase se pasan como argumentos a métodos ou funcións.

  • As instancias de estrutura pásanse por valor. Esto significa que se crea unha copia da instancia cos datos contidos na estrutura orixinal. Se cambiamos a copia, non se producen cambios na estrutura orixinal
  • As instancias de clase pásanse por referencia. Neste caso, non se crea unha copia, senón que pasamos a referencia da localización na memoria onde está a instancia. Noutras palabras, só hai unha instancia de clase, pero poden existir varias referencias que apuntan a ela

Vemos o explicado anteriormente no seguinte exemplo:

struct ExemploEstrutura {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

class ExemploClase {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
    
    func construirSaudo() -> String{
        return "Ola " + nome
    }
}

var miEstrutura = ExemploEstrutura(nome: "Lola Mento")
var miEstruturaCopia = miEstrutura
miEstruturaCopia.nome = "Pepe Viyuela"
print("Nome: \(miEstrutura.nome)") //Imprime: Nome: Lola Mento
print("Nome: \(miEstruturaCopia.nome)") //Imprime: Nome: Pepe Viyuela


var miClase = ExemploClase(nome: "Aitor Tilla")
print("Nome: \(miClase.nome)") //Imprime: Nome: Aitor Tilla
var miClaseCopia = miClase
miClaseCopia.nome = "Pepe Viyuela"
print("Nome: \(miClase.nome)") //Imprime: Nome: Pepe Viyuela
print("Nome: \(miClaseCopia.nome)") //Imprime: Nome: Pepe Viyuela

Como podemos observar:

  • Estruturas → pasan por valor: Na liña 26 copiamos miEstrutura en miEstruturaCopia. Como son estruturas, a copia é por valor (créase unha copia de miEstrutura), polo que ao modificar unha propiedade en miEstruturaCopia (liña 27) non afecta á estrutura orixinal (miEstrutura) que mantén o seu valor. Isto o vemos nas liñas 28 e 29
  • Clases → pasan por referencia: Na liña 34 copiamos miClase en miClaseCopia. Como son clases, non hai copia, o paso é por referencia, polo que ao modificar unha propiedade en miClaseCopia (liña 35) afecta á clase orixinal (miClase). Isto o vemos na liña 36, en lugar de imprimir o valor inicial de miClase.nome («Aitor Tilla», tal e como imprime na liña 33) imprime «Pepe Viyuela» (valor cambiado en miClaseCopia.nome)

Empregar estruturas ou clases? #

  • En xeral, as estruturas recoméndanse sempre que sexa posible porque son máis eficientes que as clases e máis seguras. Por defecto, emprega sempre que podas Struct
  • As clases deben usarse cando:
    • Necesitamos herdanza,
    • Só requírese unha instancia dos datos encapsulados
    • Se requirimos paso por referencia
    • Se queremos tomar medidas adicionais para liberar recursos mediante deinit{ }.

Share Your Valuable Opinions