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 = nilSolució:
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 = nilExercici 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.
Curs de Programació en Swift
Mòdul 1: Introducció a Swift
- Introducció a Swift
- Configuració de l'Entorn de Desenvolupament
- El Teu Primer Programa en Swift
- Sintaxi i Estructura Bàsica
- Variables i Constants
- Tipus de Dades
Mòdul 2: Flux de Control
Mòdul 3: Funcions i Closures
- Definició i Crida de Funcions
- Paràmetres de Funció i Valors de Retorn
- Closures
- Funcions d'Ordre Superior
Mòdul 4: Programació Orientada a Objectes
Mòdul 5: Swift Avançat
Mòdul 6: Swift i Desenvolupament iOS
- Introducció al Desenvolupament iOS
- Conceptes Bàsics de UIKit
- Storyboards i Interface Builder
- Xarxes en Swift
- Core Data
- Conceptes Bàsics de SwiftUI
