Weak Reference — definizione, sintassi e utilizzo nello sviluppo mobile

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

Weak Reference (riferimento debole) è un riferimento a un oggetto che non aumenta il suo contatore di ritenzione in ARC. Secondo Apple Swift Language Guide, 2026, i riferimenti deboli vengono dichiarati con la parola chiave weak e sono sempre opzionali. Quando l'oggetto viene deallocato, tutti i riferimenti deboli ad esso vengono automaticamente impostati a nil, prevenendo puntatori pendenti e rendendo i riferimenti deboli un meccanismo sicuro per rompere i cicli di ritenzione.

Punti chiave

  • Weak Reference — riferimento che non influisce sul retain count dell'oggetto; diventa nil quando l'oggetto viene deallocato
  • Dichiarazione — parola chiave weak prima di var; tipo sempre opzionale (?)
  • Utilizzo — delegati, chiusure, relazioni padre-figlio per rompere i cicli di ritenzione
  • Sicurezza — impostazione automatica a nil dopo la deallocazione dell'oggetto (zeroing weak)
  • Differenza da unowned — weak diventa nil ed è sicuro, unowned non diventa nil e richiede garanzie di durata

Cos'è Weak Reference?

Weak Reference è un riferimento non proprietario a un oggetto in ARC (Automatic Reference Counting). A differenza di un riferimento forte, che aumenta il retain count dell'oggetto e ne garantisce la durata, un riferimento debole permette all'oggetto di essere deallocato anche se è ancora referenziato. Dopo la deallocazione, il riferimento debole viene automaticamente impostato a nil — questo si chiama zeroing weak.

Zeroing weak è una caratteristica chiave del runtime di Swift e Objective-C. Quando il contatore di riferimenti di un oggetto raggiunge zero e l'oggetto viene deallocato, il runtime scorre tutti i riferimenti deboli a questo oggetto (memorizzati in una tabella debole speciale) e li imposta a nil. Questo garantisce che l'accesso alla memoria liberata (use-after-free) sia impossibile tramite riferimenti deboli — qualsiasi lettura restituisce nil.

Secondo Apple WWDC 2012 Session 406, i riferimenti deboli zeroing hanno eliminato un'intera classe di bug di crash relativi ai puntatori pendenti (dangling pointers), che erano comuni nella gestione manuale della memoria (MRR). In MRR, i riferimenti deboli esistevano solo come __unsafe_unretained — non venivano azzerati e l'accesso a un oggetto deallocato provocava EXC_BAD_ACCESS.

Sintassi di weak in Swift e Objective-C

Esaminiamo la sintassi per dichiarare riferimenti deboli in entrambi i linguaggi dell'ecosistema Apple. Nonostante il runtime condiviso, la sintassi differisce, ma la semantica è identica.

Swift

In Swift, i riferimenti deboli vengono dichiarati con la parola chiave weak prima di var. Il tipo deve essere sempre opzionale (Tipo?), poiché il riferimento può diventare nil in qualsiasi momento. Le costanti (let) non possono essere weak — solo le variabili.

swift
class ViewController: UIViewController {
    // weak properties: only var, only optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ le chiusure non memorizzano weak
    // ⬆️ Errore: weak può essere applicato solo a tipi class, non a closure
}

Importante: weak è applicabile solo a istanze di classe (tipi class), AnyObject e protocolli ereditati da AnyObject. Struct, enum e chiusure non possono essere weak — sono tipi valore e non partecipano ad ARC.

Objective-C

In Objective-C, le proprietà deboli vengono dichiarate usando l'attributo __weak o il modificatore weak nelle dichiarazioni di proprietà:

objective-c
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end

// Variabile locale debole
__weak MyObject *weakRef = someStrongObject;

Anche il runtime Objective-C fornisce zeroing weak, ma blocca inoltre l'uso di weak con le struct C e alcuni oggetti Core Foundation. Per questi, si usa __unsafe_unretained — senza zeroing.

Quando usare i riferimenti deboli

I riferimenti deboli non sono una soluzione universale, ma uno strumento per scenari specifici. Usare weak ovunque porta a complessità inutile e compromette la leggibilità. Esaminiamo gli scenari di uso corretti.

Delegati (pattern Delegate)

Delegati — lo scenario principale per weak. L'oggetto proprietario (es. UITableView) mantiene un riferimento forte a sé stesso, mentre il delegato (UIViewController) non dovrebbe possedere la tabella. L'SDK Apple garantisce che tutti i delegate e dataSource siano weak. Per i tuoi protocolli, usa sempre weak var delegate.

Genitore-Figlio con riferimento inverso

Quando un oggetto figlio deve referenziare il suo genitore (es. ChildViewController che accede a un coordinatore), usa un riferimento debole. Il genitore possiede il figlio (strong), il figlio osserva il genitore (weak) — il ciclo di ritenzione è eliminato.

Chiusure asincrone

Capture list [weak self] — il modo standard per evitare cicli di ritenzione nelle chiusure memorizzate come proprietà di classe. Se self può essere deallocato prima del completamento della chiusura, weak self è obbligatorio.

ScenarioWeakStrong
Delegate✅ Sempre weak❌ Retain cycle
Genitore → Figlio❌ Non necessario (genitore deve possedere)✅ Strong
Figlio → Genitore✅ Weak❌ Retain cycle
Callback asincrono✅ [weak self]❌ Rischio retain cycle
Accoppiamento forte (owned)❌ unowned✅ Strong

Regola generale: se l'oggetto A possiede B (A → B strong), allora B → A dovrebbe essere weak o unowned. La direzione dei riferimenti forti deve essere sempre dal proprietario al subordinato.

Weak vs Unowned: confronto e scenari

Sia weak che unowned non aumentano il retain count, ma differiscono nel comportamento dopo la deallocazione dell'oggetto. La scelta tra loro è una questione di garanzie di durata.

Differenze

Weak: diventa nil automaticamente, il tipo è sempre opzionale, richiede unwrap prima dell'uso. Sicuro — accedere a nil non causa crash.

Unowned: non diventa nil, il tipo è non opzionale. Se l'oggetto viene deallocato, un riferimento unowned diventa un puntatore pendente — accedervi causa un crash a runtime. Unowned presume che l'oggetto viva almeno quanto il lato referenziante.

Quando scegliere weak

Scegli weak se: l'oggetto può essere deallocato in qualsiasi momento (delegato dopo la chiusura dello schermo), non controlli la durata dell'oggetto, o sei incerto sulle garanzie. Weak è la scelta sicura universale.

Quando scegliere unowned

Scegli unowned se: l'oggetto è garantito a non essere deallocato prima dell'oggetto referenziante (es. Cliente → CartaCredito, dove la carta non esiste senza il cliente). Unowned fornisce un'API non opzionale senza unwrap, che è più comoda nel codice.

swift
class Order {
    let id: Int
    var items: [Item] = []

    init(id: Int) { self.id = id }

    // Relazione forte: Order possiede Item
    func addItem(name: String) {
        let item = Item(name: name, order: self)
        items.append(item)
    }
}

class Item {
    let name: String
    unowned let order: Order          // ✅ unowned — Item non vive senza Order

    init(name: String, order: Order) {
        self.name = name
        self.order = order
    }
}

// Esempio con weak: delegato senza garanzia di durata
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — il delegato può andarsene
}

Nell'esempio, Item usa unowned perché un elemento dell'ordine non può esistere senza l'ordine stesso — la garanzia di durata è ferrea. NetworkService usa weak perché il delegato (es. ViewController) può essere chiuso e deallocato in qualsiasi momento.

Limitazioni dei riferimenti deboli e insidie

I riferimenti deboli sono uno strumento potente, ma hanno limitazioni che è importante comprendere per un uso corretto nello sviluppo iOS.

Prestazioni di weak

I riferimenti deboli sono più lenti di quelli forti: a ogni accesso, il runtime verifica se l'oggetto è stato deallocato (lookup nella tabella debole). Nella stragrande maggioranza degli scenari, la differenza è impercettibile, ma in cicli caldi con milioni di accessi, weak può diventare un collo di bottiglia. Per scenari ad alto carico, usa strong e riorganizza l'architettura.

Weak non è applicabile ai tipi valore

Struct, enum, tuple — tipi valore che non partecipano ad ARC. Tentare di dichiarare un weak struct provoca un errore di compilazione. Per memorizzare un riferimento debole a un tipo valore, usa un wrapper in un tipo classe o una chiusura.

Weak nel multithreading

Zeroing weak è thread-safe: se un oggetto viene deallocato su un thread, il riferimento debole viene azzerato su tutti i thread atomicamente. Tuttavia, la finestra tra la lettura di un riferimento debole e il suo dereferenziamento può portare a una condizione di gara — l'oggetto viene deallocato tra l'ottenimento del riferimento debole e il suo utilizzo. Soluzione: cattura forte del riferimento debole in una variabile locale.

swift
// Race condition con weak nel multithreading
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf può essere nil tra controllo e utilizzo
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH se diventa nil
        }
    }
}

// ✅ Correzione: cattura forte durante l'uso
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — riferimento locale forte
    }
}

Nella versione sicura, weak self viene catturato, poi immediatamente unwrappato in una variabile locale forte strongSelf. Se self è ancora vivo, rimarrà vivo per la durata del blocco. Altrimenti, guard si attiva e il codice non viene eseguito. Questo idioma è il modello standard per le chiusure asincrone in Swift.

UIView e weak outlet

IBOutlet in Interface Builder dovrebbero essere weak perché la gerarchia delle viste mantiene già un riferimento forte alla subview. Duplicare un riferimento forte nel controller non crea un ciclo di ritenzione ma è ridondante. Un riferimento debole a un outlet è la raccomandazione di Apple, sebbene molti sviluppatori usino strong per semplicità di codice.

Domande frequenti

Un riferimento debole può puntare a un oggetto che non è stato ancora creato?

No, weak può puntare solo a un oggetto esistente o nil. Quando crei un nuovo oggetto, ottieni prima un riferimento forte (tramite un inizializzatore), e solo dopo puoi assegnare un riferimento debole. Un weak nil all'inizio è uno stato normale.

Perché weak funziona solo con i tipi class?

Weak si basa su ARC, che gestisce solo i tipi riferimento (classi). I tipi valore (struct, enum) vengono copiati all'assegnazione e non hanno retain count. Per relazioni deboli con tipi valore, usa chiusure o wrapper in una classe con una proprietà weak.

Come influisce weak sulle prestazioni in un ciclo?

Ogni accesso a un riferimento debole esegue un lookup nella tabella del runtime. In un ciclo con milioni di iterazioni, può essere da 2 a 5 volte più lento di un riferimento forte. Per i percorsi caldi, copia weak in una variabile locale forte prima del ciclo.

Quando un riferimento debole può diventare nil inaspettatamente?

Quando tutti i riferimenti forti all'oggetto vengono persi — alla fine dell'ambito, quando si riassegna una proprietà, o quando si chiude una schermata. In un ambiente multithread, questo può accadere tra due righe di codice. Controlla sempre i riferimenti deboli con guard let o if let.

In cosa weak differisce da __weak in Objective-C?

Semanticamente identici: entrambi forniscono zeroing weak. Differenze: Swift richiede un tipo opzionale e var, Objective-C usa un modificatore di proprietà. Objective-C supporta anche __unsafe_unretained — un riferimento debole senza zeroing (rischio di puntatore pendente).

Riepilogo

  • Weak Reference — riferimento non proprietario che non aumenta il retain count e viene automaticamente azzerato alla deallocazione
  • Sintassiweak var + tipo opzionale; solo tipi class e protocolli AnyObject
  • Zeroing weak — il runtime azzera tutti i riferimenti deboli a un oggetto deallocato, prevenendo puntatori pendenti
  • Scenari — delegati, genitore-figlio con riferimento inverso, chiusure asincrone ([weak self])
  • Weak vs Unowned — weak diventa nil (sicuro), unowned non diventa nil (rischio crash, ma non opzionale)
  • Prestazioni — weak è più lento di strong a causa del lookup nella tabella del runtime; per percorsi caldi, copia in strong
  • Raccomandazione — se non sei sicuro delle garanzie di durata, scegli weak

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