cat swift/arc-ownership-automatica.md

Lezione 24 45 min

ARC: ownership automatica ma non invisibile

Automatic Reference Counting gestisce la memoria delle classi, ma il programmatore deve capire chi possiede cosa.

Swift usa ARC, Automatic Reference Counting, per gestire la memoria degli oggetti class.

Automatico non significa magico. ARC libera un oggetto quando non esistono più riferimenti forti a quell’oggetto. Se i riferimenti forti formano un ciclo, l’oggetto può restare vivo per sempre.

Riferimenti forti

Per default, una proprietà che punta a una classe mantiene un riferimento forte.

final class Cliente {
  let nome: String

  init(nome: String) {
    self.nome = nome
  }

  deinit {
    print("Cliente rilasciato")
  }
}

var cliente: Cliente? = Cliente(nome: "Ada")
cliente = nil

Quando cliente diventa nil, se non ci sono altri riferimenti forti, l’oggetto viene rilasciato e deinit viene chiamato.

Ownership: chi possiede chi

Il punto importante è ragionare in termini di possesso.

final class OrdineCoordinator {
  let sessione: SessioneCheckout

  init(sessione: SessioneCheckout) {
    self.sessione = sessione
  }
}

Il coordinator possiede la sessione? Se si, il riferimento forte è sensato.

Se invece una relazione è solo osservazione o collegamento inverso, un riferimento forte può essere sbagliato.

deinit come strumento didattico

deinit viene chiamato quando un’istanza di classe sta per essere rilasciata.

final class Tracker {
  deinit {
    print("Tracker deinit")
  }
}

Non riempire il codice di produzione di print in deinit, ma in laboratorio è utile per capire se un oggetto viene liberato.

Struct e ARC

ARC riguarda principalmente le classi. Una struct non ha reference counting proprio come oggetto.

Ma una struct può contenere riferimenti:

struct Schermata {
  let viewModel: CheckoutViewModel
}

Se CheckoutViewModel è una classe, quel riferimento è gestito da ARC.

Riferimenti temporanei

var a: Cliente? = Cliente(nome: "Ada")
var b = a

a = nil

L’oggetto non viene rilasciato, perché b lo mantiene ancora.

b = nil

Ora può essere rilasciato.

Questo è reference counting: conta quanti riferimenti forti mantengono vivo l’oggetto.

ARC non risolve cicli

Se A mantiene B e B mantiene A, il conteggio non arriva mai a zero.

final class Utente {
  var carrello: CarrelloSessione?
}

final class CarrelloSessione {
  var utente: Utente?
}

Se entrambi sono riferimenti forti, hai un potenziale ciclo.

La prossima lezione entra nel dettaglio di weak e unowned.

Quando usare classi con più attenzione

Ogni volta che introduci una classe, chiediti:

  • chi la possiede?
  • quanto deve vivere?
  • chi può mantenerla viva?
  • esistono riferimenti inversi?
  • una closure la cattura?
  • serve davvero identità condivisa?

Queste domande evitano gran parte dei problemi ARC.

ARC e performance

Gestire reference count ha un costo. Di solito non è un problema, ma in codice molto caldo può contare.

Questo non significa evitare tutte le classi. Significa:

  • non creare oggetti inutili in loop critici;
  • preferire value types quando bastano;
  • evitare architetture piene di piccoli oggetti mutabili senza motivo.

Checklist

Con ARC ricorda:

  • le classi sono gestite da reference counting;
  • i riferimenti sono forti per default;
  • un oggetto viene liberato quando non ha più riferimenti forti;
  • deinit aiuta a osservare il rilascio;
  • i cicli forti non vengono risolti automaticamente;
  • ownership va progettata.

Esercizio

Crea due classi:

  • CheckoutViewModel;
  • CouponRepositoryInMemoria.

Aggiungi deinit a entrambe. Crea e rilascia istanze opzionali, osservando quando vengono liberate.

Poi aggiungi un riferimento da una all’altra e spiega chi possiede chi.