ARC: cos'è, principio di funzionamento di Automatic Reference Counting in iOS

Autore: IT Sectr Pubblicato: 2026-03-29 Tempo di lettura: 8 min

Automatic Reference Counting (ARC) è un sistema di gestione della memoria in Swift e Objective-C che conta automaticamente il numero di riferimenti a ciascun oggetto e lo rilascia quando il contatore raggiunge lo zero. Secondo la Documentazione Apple Swift, 2026, ARC è integrato nel compilatore e funziona in fase di compilazione, inserendo chiamate retain/release nei punti appropriati. A differenza del Garbage Collection, ARC non richiede un thread di raccolta separato e non crea pause durante l'esecuzione dell'applicazione.

Punti chiave

  • ARC — Automatic Reference Counting, sistema di gestione della memoria basato sul compilatore in Swift e Objective-C
  • Principio di funzionamento — ogni oggetto ha un contatore di riferimenti (retain count); a zero, l'oggetto viene immediatamente rilasciato
  • Qualificatori — strong, weak e unowned determinano come il riferimento influisce sul contatore e sul ciclo di vita dell'oggetto
  • Differenza da GC — ARC funziona deterministicamente in fase di compilazione, senza pause Stop-The-World o thread di raccolta in background
  • Retain Cycle — il problema principale di ARC: se due oggetti si riferiscono a vicenda tramite strong, il loro contatore non raggiunge mai lo zero

Cos'è ARC?

ARC (Automatic Reference Counting) è un meccanismo di gestione della memoria basato sul compilatore introdotto da Apple in Xcode 4.2 (2011) per Objective-C ed ereditato da Swift. A differenza della gestione manuale della memoria (Manual Retain-Release, MRR), ARC automatizza completamente le chiamate retain, release e autorelease, inserendole in fase di compilazione senza intervento dello sviluppatore.

ARC non è un garbage collector. È analisi statica con inserimento dinamico di codice: il compilatore analizza la durata degli oggetti e posiziona retain/release nei punti in cui gli oggetti vengono creati, copiati o escono dall'ambito. Il risultato è un rilascio deterministico della memoria: l'oggetto viene rimosso esattamente quando non ci sono più riferimenti che puntano ad esso, senza ritardi o pause.

Secondo la WWDC 2011 Session 323, il passaggio da MRR ad ARC ha ridotto i bug di crash legati alla memoria del 70% nelle applicazioni Apple. Gli sviluppatori hanno smesso di bilanciare manualmente retain/release, eliminando un'intera classe di perdite ed errori double-free.

Come funziona Automatic Reference Counting

Ogni oggetto in memoria ha un contatore di riferimenti (retain count). Quando un oggetto viene creato, il contatore viene impostato a 1. Quando un nuovo riferimento strong punta all'oggetto, il contatore aumenta (retain). Quando un riferimento strong scompare, il contatore diminuisce (release). Al raggiungimento dello zero, l'oggetto viene immediatamente rilasciato.

Il compilatore Swift non inserisce retain/release ad ogni assegnazione — utilizza l'analisi statica per l'ottimizzazione. Ad esempio, se è garantito che un oggetto non verrà utilizzato dopo essere stato passato, il compilatore può saltare un release/retain non necessario. Questa ottimizzazione si chiama ARC Optimization.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) initialized (retain count: 1)")
    }
    deinit {
        print("\(name) deallocated")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q esce dall'ambito
    // retain count = 1
    // p esce dall'ambito
    // retain count = 0 → deinit
}

Questo esempio mostra come ARC gestisce il contatore: assegnando q = p, il contatore aumenta; quando q esce dall'ambito, diminuisce. Quando l'ultimo riferimento strong scompare, il deinizializzatore viene chiamato immediatamente. Nessun garbage collector attende — la memoria viene liberata all'istante.

ARC vs Garbage Collection: differenze principali

ARC e Garbage Collection risolvono lo stesso problema — la gestione automatica della memoria — ma con approcci fondamentalmente diversi. La scelta tra di essi definisce l'architettura del linguaggio: Swift (ARC) vs Java/Go (GC). Esaminiamo le differenze principali.

CaratteristicaARC (Swift/ObjC)GC (Java/Go)
Momento del rilascioDeterministico: immediatamente quando il contatore arriva a zeroNon deterministico: al prossimo ciclo di raccolta
Pause di esecuzioneNessuna (retain/release inseriti in compilazione)Pause Stop-The-World (2–200 ms)
OverheadIncremento/decremento del contatore ad ogni riferimentoAttraversamento del grafo oggetti, marcatura, spazzamento
ProblemiRetain Cycle (risoluzione manuale)Frammentazione dell'heap, perdite da riferimenti dimenticati
Thread aggiuntivoNon richiestoThread del garbage collector richiesto

Il compromesso chiave: ARC fornisce durate di vita prevedibili e zero pause, ma richiede che lo sviluppatore comprenda i retain cycle e scelga correttamente weak/unowned. GC libera lo sviluppatore da queste preoccupazioni, ma al costo di pause non deterministiche e un thread aggiuntivo.

Strong, Weak e Unowned: qualificatori di riferimento in ARC

ARC definisce tre tipi di qualificatori di riferimento, ciascuno influenzando il contatore e il ciclo di vita dell'oggetto in modo diverso. Scegliere il qualificatore corretto è la base per una gestione sicura della memoria in Swift.

Strong

Strong è il qualificatore predefinito. Ogni riferimento strong aumenta il retain count dell'oggetto di 1. Finché esiste almeno un riferimento strong, l'oggetto rimane vivo. Tutte le proprietà di classe e le variabili locali in Swift sono strong per impostazione predefinita. I riferimenti strong creano una relazione di possesso: l'oggetto A possiede l'oggetto B.

Weak

Weak è un riferimento che non aumenta il retain count. Un oggetto può essere rilasciato anche se un riferimento weak punta ad esso. Dopo il rilascio, il riferimento weak viene automaticamente impostato a nil. I riferimenti weak sono sempre dichiarati come var con tipo opzionale (?). Sono usati per rompere i retain cycle, specialmente nel pattern delegate.

Unowned

Unowned è un riferimento non possessivo che, come weak, non aumenta il retain count. Tuttavia, un riferimento unowned non viene impostato a nil dopo il rilascio — accedere a un oggetto rilasciato causa un crash. Unowned viene usato quando è garantito che l'oggetto viva almeno quanto l'oggetto referenziante. I casi tipici sono le closures e le relazioni genitore-figlio con durata di vita garantita.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) deallocated") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — non possiede
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) deallocated") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer e CreditCard entrambi rilasciati — nessun retain cycle

Qui CreditCard usa un riferimento unowned a Customer. Customer possiede la carta (strong), e la carta non possiede il cliente (unowned). Quando Customer viene rilasciato, entrambi gli oggetti vengono rilasciati — nessun retain cycle si verifica. Se card.customer fosse strong, il ciclo bloccherebbe il rilascio.

Problemi comuni di ARC e loro soluzioni

Nonostante l'automazione, ARC non è una panacea. Gli sviluppatori incontrano diversi problemi tipici che richiedono la comprensione del meccanismo interno di gestione della memoria.

Retain Cycle nelle Closure

Le closures in Swift catturano le variabili esterne per riferimento strong. Se una closure viene assegnata a una proprietà di classe e cattura self — si verifica un retain cycle: la classe trattiene la closure, la closure trattiene self. La soluzione è una lista di cattura con weak o unowned.

swift
class NetworkManager {
    var completionHandler: ((Data?) -> Void)?
    var data: Data?

    func fetchData() {
        completionHandler = { [weak self] result in
            guard let self else { return }
            self.data = result
            self.processResult()
        }
    }

    func processResult() { }
}

La lista di cattura [weak self] crea un riferimento weak a self all'interno della closure. Questo rompe il potenziale retain cycle. Guard let self garantisce che l'oggetto sia vivo prima di eseguire il codice. weak self è la pratica standard per le closures asincrone in Swift.

Prestazioni di retain/release

Sebbene retain/release siano operazioni leggere, nei loop intensivi gli incrementi/decrementi frequenti del contatore aggiungono overhead. In Swift 5.9+, il compilatore utilizza un'ottimizzazione che rimuove retain/release ridondanti se l'analizzatore dimostra che sono sicuri. Tuttavia, in Objective-C, retain/release possono ancora essere un collo di bottiglia in scenari ad alto carico con milioni di chiamate al secondo.

Autorelease Pool

Autorelease Pool è un meccanismo di rilascio differito utilizzato in Objective-C e in alcuni scenari Swift. Gli oggetti vengono inseriti nel pool e ricevono release quando il pool viene svuotato. Nei loop con molti oggetti temporanei (ad esempio, analisi JSON), creare un autoreleasepool personalizzato riduce il consumo di picco di memoria.

Domande frequenti

In cosa ARC differisce dalla gestione manuale della memoria (MRR)?

Nella gestione manuale (MRR), lo sviluppatore chiamava esplicitamente retain, release e autorelease. ARC inserisce automaticamente queste chiamate in fase di compilazione, eliminando il rischio di double-free, perdite da release dimenticato ed errori di bilanciamento retain/release.

ARC può funzionare con codice C/C++?

ARC gestisce solo oggetti Objective-C e classi Swift. Per strutture e puntatori C/C++, ARC non si applica — questi oggetti sono gestiti manualmente o tramite smart pointer C++ (shared_ptr, unique_ptr). Anche gli oggetti Core Foundation (CFString, CGColor) non sono coperti da ARC.

Quando usare weak e quando usare unowned?

weak — quando l'oggetto può essere rilasciato prima dell'oggetto referenziante (delegati, closures asincrone). unowned — quando è garantito che l'oggetto viva almeno quanto il referenziante (genitore-figlio dove il figlio non può esistere senza il genitore). In caso di dubbio, scegli weak.

Cosa sono i tipi esistenziali e come influenzano ARC?

I tipi esistenziali (protocollo come tipo) in Swift incapsulano il valore in un contenitore speciale (contenitore esistenziale). Questo aumenta il numero di retain/release ai confini dei protocolli. In Swift 5.7+, i tipi di risultato opachi e i parametri some riducono l'overhead eliminando il contenitore.

Come verificare il retain count in Swift?

Non esiste un'API diretta per leggere il retain count in Swift — è considerato un dettaglio implementativo. Per la diagnostica, usa Instruments (Allocations, Leaks) o il Debugger di Memoria in Xcode. Questi strumenti mostrano il numero di istanze di classe vive e le catene di ritenzione.

Riepilogo

  • ARC — sistema di gestione della memoria basato sul compilatore per Swift e Objective-C che funziona tramite conteggio dei riferimenti
  • Principio — ogni oggetto ha un retain count; a zero, l'oggetto viene rilasciato immediatamente e deterministicamente
  • Differenza da GC — ARC funziona senza thread in background o pause Stop-The-World, ma richiede il controllo dei retain cycle
  • Strong — aumenta il contatore; weak e unowned non lo aumentano, ma unowned non viene annullato al rilascio
  • Closure — la causa principale dei retain cycle in Swift; la lista di cattura [weak self] è la soluzione standard
  • Autorelease Pool — meccanismo di rilascio differito per oggetti temporanei in loop e scenari personalizzati
  • Diagnostica — Xcode Memory Debugger, Instruments e LeakCanary (tramite ponte ObjC) per trovare problemi

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche