La gestió de memòria és un aspecte crucial en qualsevol llenguatge de programació, i Swift no és una excepció. En aquest tema, explorarem com Swift gestiona la memòria, incloent conceptes com el comptatge automàtic de referències (ARC), cicles de referència i com evitar-los.

Continguts

Comptatge Automàtic de Referències (ARC)

Swift utilitza el Comptatge Automàtic de Referències (ARC) per gestionar la memòria dels objectes. ARC fa un seguiment del nombre de referències fortes a cada instància d'objecte i allibera la memòria quan ja no hi ha referències fortes a l'objecte.

Com Funciona ARC

  • Referència Forta: Cada vegada que una instància d'una classe és assignada a una variable, constant o propietat, es crea una referència forta.
  • Increment de Comptador: ARC incrementa el comptador de referències cada vegada que es crea una nova referència forta.
  • Decrement de Comptador: ARC decrementa el comptador de referències cada vegada que una referència forta es destrueix.
  • Alliberament de Memòria: Quan el comptador de referències arriba a zero, ARC allibera la memòria ocupada per l'objecte.

Exemple

class Persona {
    var nom: String
    
    init(nom: String) {
        self.nom = nom
    }
}

var persona1: Persona? = Persona(nom: "Joan")
var persona2: Persona? = persona1

persona1 = nil
persona2 = nil
// En aquest punt, l'objecte Persona és alliberat de la memòria perquè no hi ha referències fortes.

Cicles de Referència

Un cicle de referència es produeix quan dues o més instàncies es referencien mútuament, impedint que ARC alliberi la memòria d'aquestes instàncies.

Exemple de Cicle de Referència

class Persona {
    var nom: String
    var mascota: Mascota?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Mascota {
    var nom: String
    var propietari: Persona?
    
    init(nom: String) {
        self.nom = nom
    }
}

var joan: Persona? = Persona(nom: "Joan")
var fido: Mascota? = Mascota(nom: "Fido")

joan?.mascota = fido
fido?.propietari = joan

joan = nil
fido = nil
// En aquest punt, l'objecte Persona i l'objecte Mascota no són alliberats de la memòria perquè hi ha un cicle de referència.

Referències Febles i Sense Propietari

Per evitar cicles de referència, podem utilitzar referències febles (weak) i referències sense propietari (unowned).

Referències Febles

Les referències febles no incrementen el comptador de referències. S'utilitzen quan una referència pot ser nil en algun moment.

class Persona {
    var nom: String
    var mascota: Mascota?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Mascota {
    var nom: String
    weak var propietari: Persona?
    
    init(nom: String) {
        self.nom = nom
    }
}

var joan: Persona? = Persona(nom: "Joan")
var fido: Mascota? = Mascota(nom: "Fido")

joan?.mascota = fido
fido?.propietari = joan

joan = nil
fido = nil
// En aquest punt, l'objecte Persona i l'objecte Mascota són alliberats de la memòria perquè no hi ha cicle de referència.

Referències Sense Propietari

Les referències sense propietari (unowned) tampoc incrementen el comptador de referències. S'utilitzen quan una referència mai serà nil després de ser inicialitzada.

class Persona {
    var nom: String
    var targeta: Targeta?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Targeta {
    var numero: Int
    unowned var propietari: Persona
    
    init(numero: Int, propietari: Persona) {
        self.numero = numero
        self.propietari = propietari
    }
}

var joan: Persona? = Persona(nom: "Joan")
var targeta: Targeta? = Targeta(numero: 1234, propietari: joan!)

joan?.targeta = targeta

joan = nil
// En aquest punt, l'objecte Persona i l'objecte Targeta són alliberats de la memòria perquè no hi ha cicle de referència.

Exemples Pràctics

Exemple 1: Evitant Cicles de Referència amb weak

class Node {
    var valor: Int
    weak var pare: Node?
    var fills: [Node] = []
    
    init(valor: Int) {
        self.valor = valor
    }
    
    func afegirFill(_ fill: Node) {
        fills.append(fill)
        fill.pare = self
    }
}

let arrel = Node(valor: 1)
let fill1 = Node(valor: 2)
let fill2 = Node(valor: 3)

arrel.afegirFill(fill1)
arrel.afegirFill(fill2)

// No hi ha cicle de referència perquè la referència al pare és feble.

Exemple 2: Evitant Cicles de Referència amb unowned

class Persona {
    var nom: String
    var targeta: Targeta?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Targeta {
    var numero: Int
    unowned var propietari: Persona
    
    init(numero: Int, propietari: Persona) {
        self.numero = numero
        self.propietari = propietari
    }
}

var joan: Persona? = Persona(nom: "Joan")
var targeta: Targeta? = Targeta(numero: 1234, propietari: joan!)

joan?.targeta = targeta

joan = nil
// No hi ha cicle de referència perquè la referència al propietari és sense propietari.

Exercicis

Exercici 1: Identificar Cicles de Referència

Descripció: Analitza el següent codi i identifica si hi ha un cicle de referència. Si n'hi ha, corregeix-lo.

class Autor {
    var nom: String
    var llibre: Llibre?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Llibre {
    var titol: String
    var autor: Autor?
    
    init(titol: String) {
        self.titol = titol
    }
}

var autor: Autor? = Autor(nom: "Gabriel Garcia Marquez")
var llibre: Llibre? = Llibre(titol: "Cien Años de Soledad")

autor?.llibre = llibre
llibre?.autor = autor

autor = nil
llibre = nil

Solució:

class Autor {
    var nom: String
    var llibre: Llibre?
    
    init(nom: String) {
        self.nom = nom
    }
}

class Llibre {
    var titol: String
    weak var autor: Autor? // Canviem a weak per evitar el cicle de referència
    
    init(titol: String) {
        self.titol = titol
    }
}

var autor: Autor? = Autor(nom: "Gabriel Garcia Marquez")
var llibre: Llibre? = Llibre(titol: "Cien Años de Soledad")

autor?.llibre = llibre
llibre?.autor = autor

autor = nil
llibre = nil

Exercici 2: Crear una Jerarquia d'Objectes Sense Cicles de Referència

Descripció: Crea una jerarquia d'objectes que representi una empresa amb empleats i projectes. Assegura't que no hi hagi cicles de referència.

Solució:

class Empleat {
    var nom: String
    var projectes: [Projecte] = []
    
    init(nom: String) {
        self.nom = nom
    }
    
    func afegirProjecte(_ projecte: Projecte) {
        projectes.append(projecte)
        projecte.empleat = self
    }
}

class Projecte {
    var nom: String
    weak var empleat: Empleat?
    
    init(nom: String) {
        self.nom = nom
    }
}

let empleat = Empleat(nom: "Anna")
let projecte1 = Projecte(nom: "Projecte A")
let projecte2 = Projecte(nom: "Projecte B")

empleat.afegirProjecte(projecte1)
empleat.afegirProjecte(projecte2)

// No hi ha cicle de referència perquè la referència a l'empleat és feble.

Conclusió

En aquesta secció, hem après com Swift gestiona la memòria utilitzant el Comptatge Automàtic de Referències (ARC) i com evitar cicles de referència utilitzant referències febles (weak) i sense propietari (unowned). Aquests conceptes són fonamentals per escriure codi eficient i lliure de fuites de memòria. En el següent tema, explorarem la concurrència en Swift, un altre aspecte crucial per desenvolupar aplicacions eficients i responsives.

© Copyright 2024. Tots els drets reservats