Strong Reference (riferimento forte): cos'è, meccanismo di funzionamento e ARC

Autore: IT Sectr Pubblicato: 2026-03-30 Tempo di lettura: 9 min

Strong Reference (riferimento forte) è un meccanismo standard di gestione della memoria in cui un oggetto rimane in memoria finché almeno un riferimento attivo punta ad esso. A differenza dei riferimenti deboli, un riferimento forte incrementa il contatore di riferimenti dell’oggetto e impedisce il suo rilascio automatico. Secondo la Apple Developer Documentation, ARC gestisce automaticamente il ciclo di vita degli oggetti in Swift e Objective-C. Comprendere il funzionamento dei riferimenti forti è fondamentale per prevenire perdite di memoria e dipendenze cicliche nelle applicazioni mobili.

Punti chiave

  • Strong Reference — un riferimento che mantiene un oggetto in memoria incrementando il suo retain count di 1.
  • ARC inserisce automaticamente le operazioni retain e release, eliminando la gestione manuale della memoria in Swift e Objective-C.
  • Retain cycle si verifica quando due oggetti si riferiscono reciprocamente tramite riferimenti forti — la memoria non viene mai liberata.
  • Weak Reference non incrementa il contatore di riferimenti e diventa automaticamente nil quando l’oggetto viene deallocato.
  • Unowned Reference non incrementa il contatore ma presuppone che l’oggetto non viva più a lungo del suo proprietario.

Cos’è Strong Reference?

Strong Reference è un tipo di riferimento a un oggetto che impedisce la sua distruzione da parte del garbage collector o del sistema di gestione della memoria. Finché esiste almeno un riferimento forte all’oggetto, la sua memoria non viene liberata. Questo è il meccanismo di base su cui si basano ARC in Swift e Objective-C e la garbage collection in Java e Kotlin.

Il concetto di riferimento forte è fondamentale per tutti i linguaggi con gestione automatica della memoria. Nei sistemi con ARC, ogni riferimento forte incrementa il contatore di riferimenti dell’oggetto. Quando il contatore arriva a zero, l’oggetto viene immediatamente deallocato. In Java e Kotlin con garbage collection, un riferimento forte garantisce che l’oggetto sia raggiungibile e non verrà raccolto dal GC.

Secondo WWDC 2021, circa il 35% delle perdite di memoria nelle applicazioni iOS sono correlate all’uso errato di riferimenti forti e retain cycle. Nello sviluppo Android, le perdite attraverso riferimenti forti impliciti in closures e callback sono la seconda causa più comune di problemi di memoria dopo Context Leak.

Per lavorare efficacemente con la memoria, è necessario comprendere la differenza tra riferimenti strong, weak e unowned e scegliere il tipo di riferimento appropriato in base alla proprietà e al ciclo di vita degli oggetti.

Come ARC ha cambiato la gestione della memoria

Prima di ARC, gli sviluppatori chiamavano manualmente retain e release per ogni oggetto, causando numerosi errori. ARC, introdotto da Apple nel 2011 con LLVM 3.0, ha automatizzato questo processo analizzando il grafo di proprietà al momento della compilazione. Il compilatore stesso inserisce le chiamate retain, release e autorelease dove necessario.

Secondo Clang Static Analyzer, l’introduzione di ARC ha ridotto i bug relativi alla memoria nelle applicazioni iOS del 70%. Per gli sviluppatori, ciò significa che la gestione della memoria è diventata più sicura, ma allo stesso tempo è nata la necessità di capire come funzionano internamente i riferimenti forti — per evitare retain cycle.

In Kotlin e Java, il garbage collector svolge il ruolo di ARC, ma il principio del riferimento forte rimane lo stesso: GC Roots sono i punti di ingresso attraverso cui gli oggetti sono mantenuti da riferimenti forti. Finché un oggetto è raggiungibile attraverso una catena di riferimenti forti da un GC Root, non verrà raccolto.

Come funziona Strong Reference in ARC?

ARC (Automatic Reference Counting) funziona contando i riferimenti per ogni oggetto nell’heap. Quando viene creato un nuovo riferimento forte a un oggetto, il contatore aumenta (retain). Quando il riferimento viene distrutto o sovrascritto, il contatore diminuisce (release). Quando il contatore raggiunge zero, l’oggetto viene immediatamente rimosso dalla memoria.

Consideriamo un esempio in Swift. Quando viene creata un’istanza di una classe, ARC alloca la memoria e imposta il retain count a 1. Ogni nuova assegnazione a un’altra variabile incrementa il contatore. Quando la variabile esce dall’ambito, il contatore diminuisce:

swift
class ProfileViewController {
    var nameLabel: String?
    var avatarImage: UIImage?

    func loadProfile() {
        // retain count = 1 per una nuova istanza
        let user = User(name: "Ivan")
        // retain count = 2 dopo l’assegnazione di nameLabel
        nameLabel = user.name
        // uscita dal metodo — user esce dall’ambito, retain count = 1
    }
}

In questo codice, ARC garantisce che l’oggetto User rimanga in memoria finché almeno un riferimento forte punta ad esso. Quando la funzione loadProfile termina, la variabile locale user viene distrutta, ma nameLabel mantiene ancora l’oggetto. La memoria verrà liberata solo quando nameLabel cesserà di esistere o verrà sovrascritta.

In Kotlin, un comportamento simile è fornito attraverso GC Roots. Finché esiste una catena tracciabile di riferimenti forti da una radice del garbage collector (ad esempio, un campo statico o un thread attivo), l’oggetto rimane in memoria. La differenza è che il GC non libera la memoria istantaneamente — avviene in modo asincrono dopo l’analisi di raggiungibilità.

Quando avviene il rilascio della memoria

In ARC, il rilascio avviene in modo sincrono quando il contatore raggiunge zero. In Swift e Objective-C, sai esattamente quando l’oggetto verrà rimosso. In Kotlin e Java, il momento del rilascio è imprevedibile, ma ciò è compensato da uno schema più flessibile per rilevare le dipendenze cicliche a livello del garbage collector.

Retain Cycles e perdite di memoria

Retain cycle (ciclo di ritenzione) — una situazione in cui due o più oggetti hanno riferimenti forti reciproci. Di conseguenza, il loro retain count non scende mai a zero e la memoria non viene mai liberata, anche dopo che gli oggetti non sono più necessari all’applicazione.

Un esempio classico: un view controller padre mantiene un oggetto figlio con un riferimento forte, e questo a sua volta mantiene il padre con un riferimento forte. Ciò è tipico in situazioni con delegati, closures ed espressioni lambda annidate. Secondo Instruments Leaks, i retain cycle rappresentano fino al 60% di tutte le perdite di memoria nelle applicazioni che utilizzano ARC.

swift
class ParentViewController: UIViewController {
    var child: ChildViewController?

    func setupChild() {
        child = ChildViewController()
        // retain cycle: il padre mantiene il figlio, il figlio mantiene il padre tramite closure
        child?.onEvent = {
            self.handleEvent()
        }
    }

    func handleEvent() {}
}

Il problema qui è che la closure onEvent cattura self (ParentViewController) con un riferimento forte, e ParentViewController stesso mantiene child con un riferimento forte. Entrambi gli oggetti non verranno mai liberati. La soluzione è utilizzare weak self nella closure per rompere il ciclo.

In Kotlin, cicli simili si verificano quando si utilizzano lambda che catturano oggetti esterni. Il garbage collector della JVM può rilevare questi cicli col tempo, ma solo se gli oggetti sono irraggiungibili dai GC Roots. Se il ciclo è legato a un thread attivo o a un contesto UI, la perdita persiste per tutta la vita dell’applicazione.

Strong vs Weak vs Unowned Reference

Comprendere la differenza tra i tipi di riferimento è la chiave per una gestione sicura della memoria. Strong Reference incrementa il retain count. Weak Reference non incrementa il retain count e diventa automaticamente nil quando l’oggetto viene deallocato. Unowned Reference non incrementa il retain count ma non viene azzerato — accedervi dopo la deallocazione provoca un crash.

Tipo di riferimentoRetain countSicurezzaQuando usare
Strong+1Sicuro (predefinito)Proprietà dell’oggetto, relazione padre → figlio
WeakNon cambiaAuto-azzeramento (sicuro)Delegati, callbacks, riferimenti inversi
UnownedNon cambiaRischio di crash in caso di accesso tardivoQuando l’oggetto vive garantitamente più a lungo del proprietario

La scelta del tipo di riferimento è dettata dalla relazione di proprietà. Se l’oggetto B fa parte di A e non può esistere senza di esso — usa Strong. Se B può esistere indipendentemente e fa riferimento ad A per notifiche — usa Weak. Unowned è usato raramente — solo quando la durata dell’oggetto figlio non supera strettamente quella del genitore.

Regola pratica di selezione

Apple Developer Documentation raccomanda: per impostazione predefinita, usa strong per tutte le relazioni di proprietà. Se devi evitare un retain cycle — determina quale riferimento dovrebbe essere debole. Di solito è il riferimento inverso nella gerarchia (figlio → padre). In Kotlin, un ruolo simile è svolto da WeakReference di java.lang.ref, utilizzato per cache e pattern observer.

Come risolvere i problemi con i riferimenti forti

Rilevare i retain cycle è il primo passo. Il secondo è eliminarli correttamente. Lo strumento principale per rompere i cicli di riferimenti forti è sostituire uno dei riferimenti con weak o unowned. Nei linguaggi con garbage collection, viene inoltre utilizzato WeakReference con controllo manuale di null prima di ogni accesso.

In Swift e Objective-C, la correzione più comune è aggiungere [weak self] nelle closure. Ciò garantisce che la closure non trattenga l’oggetto dopo la sua deallocazione. In Kotlin, wrapper WeakReference o pulizia esplicita dei riferimenti in onDestroy sono utilizzati per scopi simili.

swift
class NetworkService {
    func fetchData(completion: @escaping (Data?) -> Void) {
        // cattura con weak self — retain cycle eliminato
        URLSession.shared.dataTask(
            with: URL(string: "https://api.example.com")!
        ) { [weak self] data, response, error in
            guard let self else { return }
            completion(data)
        }.resume()
    }
}

In questo esempio, [weak self] garantisce che NetworkService non sia trattenuto dalla closure dopo che non è più necessario. Se self viene deallocato prima del completamento della richiesta — guard let self else { return } esce dalla closure senza chiamare completion.

Per la diagnosi dei retain cycle, usa Instruments Leaks per iOS o Android Profiler + LeakCanary per Android. Questi strumenti mostrano il grafo esatto di ritenzione e indicano quale riferimento forte impedisce la deallocazione dell’oggetto. La profilazione regolare della memoria dovrebbe far parte del pipeline CI/CD di qualsiasi progetto mobile.

Strong Reference in Swift e Kotlin — confronto

Swift e Kotlin utilizzano meccanismi di gestione della memoria fondamentalmente diversi, ma il concetto di riferimento forte esiste in entrambi. Swift utilizza ARC con rilascio sincrono al raggiungimento di retain count = 0. Kotlin utilizza un GC di tracciamento che pulisce in modo asincrono gli oggetti irraggiungibili.

ParametroSwift (ARC)Kotlin (JVM GC)
MeccanismoConteggio dei riferimenti (retain count)Tracciamento di raggiungibilità (GC Roots)
RilascioSincrono (quando il contatore arriva a zero)Asincrono (per ciclo GC)
Retain cycleNon rilevato automaticamenteIl GC può rilevarlo, ma non immediatamente
Riferimento deboleweak (auto-azzeramento)WeakReference (controllo manuale)

La principale differenza pratica: in Swift, un retain cycle è una perdita garantita. In Kotlin, il GC può rompere il ciclo se gli oggetti sono irraggiungibili dalla radice, ma la durata degli oggetti persi rimane imprevedibile. Pertanto, in entrambi i linguaggi, la strategia migliore è evitare i cicli di riferimenti forti in fase di progettazione.

Per Swift, usa weak nei pattern di delegato e nelle closure. Per Kotlin, usa WeakReference o componenti Lifecycle-aware che cancellano automaticamente i riferimenti quando il proprietario viene distrutto. In entrambi gli approcci, l’obiettivo è lo stesso — eliminare i riferimenti forti dove creano una catena di ritenzione infrangibile.

Domande frequenti

In cosa Strong Reference differisce da Weak Reference?

Strong Reference incrementa il retain count dell’oggetto e impedisce il suo rilascio finché il riferimento esiste. Weak Reference non modifica il retain count e diventa automaticamente nil quando l’oggetto viene rimosso dalla memoria. I riferimenti forti sono usati per la proprietà, quelli deboli per connessioni inverse e delegati.

Cos’è un retain cycle e perché è pericoloso?

Retain cycle — un blocco reciproco in cui due oggetti si mantengono a vicenda con riferimenti forti. Il loro retain count non scende mai a zero, la memoria non viene liberata. Ciò porta a perdite di memoria: gli oggetti rimangono nell’heap per sempre, l’applicazione consuma sempre più risorse e alla fine si blocca con OutOfMemory.

Come rilevare un retain cycle in un’applicazione iOS?

Usa Instruments Leaks da Xcode — esegui la profilazione con il modello Leaks, esegui uno scenario nell’app e verifica gli indicatori di perdita. Per una diagnosi precisa, passa alla scheda Cycles & Roots — mostra il grafo dei riferimenti forti reciproci che formano un ciclo infrangibile.

Quando usare Unowned invece di Weak?

Unowned va usato quando la durata dell’oggetto figlio garantitamente non supera quella del genitore — ad esempio, quando si lega un oggetto a un ambito strettamente definito. In caso di dubbio, usa Weak, poiché l’accesso a un riferimento unowned liberato provoca un crash dell’applicazione.

I riferimenti forti influiscono sulle prestazioni dell’applicazione?

Indirettamente — sì. Ogni retain e release in ARC è un’operazione atomica con overhead. Con un gran numero di oggetti in cicli, ciò può influire sulle prestazioni. Tuttavia, il problema principale non è la velocità di ARC, ma le perdite di memoria dovute a un tipo di riferimento male scelto.

Riepilogo

  • Strong Reference è il meccanismo di base della proprietà degli oggetti, mantenendoli in memoria incrementando il retain count.
  • ARC automatizza la gestione della memoria in Swift e Objective-C, eliminando retain e release manuali, ma non protegge dai retain cycle.
  • Retain cycle si verifica con riferimenti forti reciproci — questa è la principale causa di perdite di memoria nei sistemi ARC.
  • I riferimenti Weak e Unowned rompono i cicli di riferimenti forti senza incrementare il retain count.
  • La scelta del tipo di riferimento è determinata dalla relazione di proprietà: Strong per padre→figlio, Weak o Unowned per figlio→padre.
  • Instruments Leaks e LeakCanary sono i principali strumenti per rilevare riferimenti forti problematici in iOS e Android.
  • Progetta il grafo di proprietà in anticipo — è più economico che correggere le perdite di memoria dopo il rilascio dell’applicazione.

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