Weak Reference — ce este, sintaxa și aplicarea în dezvoltarea mobilă

Autor: IT Sectr Publicat: 2026-03-30 Timp de citire: 9 min

Weak Reference (referință slabă) — este o referință la un obiect care nu crește contorul său de reținere în ARC. Conform Apple Swift Language Guide, 2026, referințele weak se declară cu cuvântul cheie weak și au întotdeauna un tip opțional. Când obiectul este eliberat, toate referințele weak către el sunt setate automat la nil, ceea ce previne pointerii dangling și face referințele slabe un mecanism sigur pentru ruperea retain cycle.

Principalele

  • Weak Reference — referință care nu afectează retain count-ul obiectului; la eliberarea obiectului se anulează
  • Declarare — cuvântul cheie weak înainte de var; tipul întotdeauna opțional (?)
  • Aplicare — delegați, closure-uri, relații parent-child pentru ruperea retain cycle
  • Siguranță — setare automată la nil după dezalocarea obiectului (zeroing weak)
  • Diferența de unowned — weak se anulează și este sigur, unowned nu se anulează și necesită garanție de viață

Ce este Weak Reference?

Weak Reference — este o referință neposesivă la un obiect în ARC (Automatic Reference Counting). Spre deosebire de referința strong, care crește retain count-ul obiectului și garantează viața acestuia, referința weak permite obiectului să fie eliberat chiar dacă mai există referințe către el. După eliberare, referința weak este setată automat la nil — aceasta se numește zeroing weak.

Zeroing weak — caracteristica cheie a runtime-ului Swift și Objective-C. Când contorul de referințe al obiectului ajunge la zero și obiectul este dezalocat, runtime-ul parcurge toate referințele weak către acest obiect (stocate într-un tabel weak special) și le setează la nil. Aceasta garantează că accesările la memoria eliberată (use-after-free) sunt imposibile prin referințe weak — orice citire returnează nil.

Conform Apple WWDC 2012 Session 406, referințele zeroing weak au eliminat o întreagă clasă de bug-uri de crash asociate cu pointerii dangling, care erau răspândite în gestionarea manuală a memoriei (MRR). În MRR referințele slabe existau doar ca __unsafe_unretained — ele nu se anulau, iar accesarea unui obiect eliberat ducea la EXC_BAD_ACCESS.

Sintaxa weak în Swift și Objective-C

Să examinăm sintaxa declarării referințelor weak în ambele limbi ale ecosistemului Apple. În ciuda runtime-ului comun, sintaxa diferă, dar semantica este identică.

Swift

În Swift referințele weak se declară cu cuvântul cheie weak înainte de var. Tipul trebuie să fie întotdeauna opțional (Type?), deoarece referința se poate anula în orice moment. Constantele (let) nu pot fi weak — doar variabilele.

swift
class ViewController: UIViewController {
    // proprietăți weak: doar var, doar optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ closure-urile weak nu stochează
    // ⬆️ Eroare: weak se poate aplica doar class-types, nu la closure
}

Important: weak se aplică doar instanțelor de clase (class-types), AnyObject și protocoalelor moștenite de la AnyObject. Struct, enum și closure nu pot fi weak — ele sunt tipuri de valoare și nu participă la ARC.

Objective-C

În Objective-C proprietățile weak se declară prin atributul __weak sau modificatorul weak în property:

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

// Variabilă weak locală
__weak MyObject *weakRef = someStrongObject;

Runtime-ul Objective-C oferă de asemenea zeroing weak, dar suplimentar blochează utilizarea weak cu structuri C și unele obiecte Core Foundation. Pentru ele se aplică __unsafe_unretained — fără zeroing.

Când să aplici referințe slabe

Referințele slabe — nu sunt o soluție universală, ci un instrument pentru scenarii specifice. Folosirea weak peste tot duce la complexitate excesivă și degradează lizibilitatea. Să examinăm scenariile corecte de aplicare.

Delegați (Delegate pattern)

Delegate — scenariul principal pentru weak. Obiectul-proprietar (de exemplu, UITableView) deține o referință strong către sine, iar delegatul (UIViewController) nu trebuie să posede tabelul. Apple SDK garantează că toți delegate și dataSource sunt weak. Pentru propriile protocoale folosește întotdeauna weak var delegate.

Parent-Child cu feedback

Când obiectul copil trebuie să se refere la părinte (de exemplu, ChildViewController pentru acces la coordinator), folosește o referință weak. Părintele posedă copilul (strong), copilul observă părintele (weak) — retain cycle exclus.

Closure-uri asincrone

Capture list [weak self] — modalitatea standard de a evita retain cycle în closure-uri stocate ca proprietăți ale clasei. Dacă self poate fi eliberat înainte de finalizarea closure-ului — weak self este obligatoriu.

ScenariuWeakStrong
Delegate✅ Întotdeauna weak❌ Retain cycle
Parent → Child❌ Nu e necesar (părintele trebuie să posede)✅ Strong
Child → Parent✅ Weak❌ Retain cycle
Callback asincron✅ [weak self]❌ Risc retain cycle
Legătură puternică (owned)❌ unowned✅ Strong

Regula generală: dacă obiectul A posedă B (A → B strong), atunci B → A trebuie să fie weak sau unowned. Direcția referințelor strong trebuie să fie întotdeauna de la proprietar la subordonat.

Weak vs Unowned: comparație și scenarii

Atât weak cât și unowned nu cresc retain count-ul, dar se comportă diferit după dezalocarea obiectului. Alegerea dintre ele este o chestiune de garanții de viață.

Diferențe

Weak: se anulează automat (nil), tipul întotdeauna optional, necesită unwrap înainte de utilizare. Sigur — accesarea nil nu provoacă crash.

Unowned: nu se anulează, tipul non-optional. Dacă obiectul a fost eliberat, referința unowned devine un pointer dangling — accesarea ei provoacă runtime crash. Unowned presupune că obiectul trăiește cel puțin cât partea care se referă la el.

Când să alegi weak

Weak alege dacă: obiectul poate fi dezalocat în orice moment (delegat după închiderea ecranului), nu controlezi durata de viață a obiectului sau ai îndoieli privind garanțiile. Weak — alegerea sigură universală.

Când să alegi unowned

Unowned alege dacă: obiectul garantat nu poate fi eliberat înaintea celui care se referă la el (de exemplu, Customer → CreditCard, unde cardul nu există fără client). Unowned oferă API non-optional fără unwrap, ceea ce este mai comod în cod.

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

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

    // Legătură puternică: Order posedă 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 nu trăiește fără Order

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

// Exemplu cu weak: delegat fără garanție de viață
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — delegatul poate pleca
}

În exemplu, Item folosește unowned, deoarece elementul comenzii nu poate exista fără comanda însăși — garanția de viață este de fier. NetworkService folosește weak, deoarece delegatul (de exemplu, ViewController) poate fi închis și eliberat în orice moment.

Limitări ale referințelor weak și capcane

Referințele slabe — un instrument puternic, dar au limitări care trebuie înțelese pentru aplicarea corectă în dezvoltarea iOS.

Performanța weak

Referințele slabe sunt mai lente decât strong: la fiecare acces runtime-ul verifică dacă obiectul a fost eliberat (lookup în tabela weak). În marea majoritate a scenariilor diferența este insesizabilă, dar în bucle fierbinți cu milioane de accesări weak poate fi un blocaj. Pentru scenarii cu încărcare mare folosește strong și reorganizează arhitectura.

Weak nu se aplică tipurilor de valoare

Struct, enum, tuple — tipuri de valoare care nu participă la ARC. Încercarea de a declara weak struct duce la eroare de compilare. Pentru stocarea unei referințe slabe la un tip de valoare folosește un wrapper în class-type sau un closure.

Weak în multi-threading

Zeroing weak este thread-safe: dacă obiectul este eliberat pe un fir, referința weak este anulată pe toate firele atomic. Totuși, intervalul dintre citirea referinței weak și dereferențiere poate duce la o condiție de cursă — obiectul este eliberat între obținerea referinței weak și utilizarea ei. Soluția: strong-capturarea referinței slabe într-o variabilă locală.

swift
// Race condition cu weak în multi-threading
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf poate fi nil între verificare și utilizare
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH dacă a devenit nil
        }
    }
}

// ✅ Corecție: capturare strong pe durata utilizării
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — referință strong locală
    }
}

În varianta sigură, weak self este capturat, apoi imediat desfăcut într-o variabilă strong locală strongSelf. Dacă self este încă viu — va rămâne viu pe durata executării blocului. Dacă nu — guard se activează și codul nu se execută. Acest idiom — modelul standard pentru closure-uri asincrone în Swift.

UIView și weak outlet

IBOutlet în Interface Builder trebuie să fie weak, deoarece ierarhia de view deține deja o referință strong la subview. Duplicarea referinței strong în controler nu creează retain cycle, dar este redundantă. Referința slabă la outlet — recomandarea Apple, deși mulți dezvoltatori folosesc strong pentru simplificarea codului.

Întrebări frecvente

Poate o referință weak să indice către un obiect care nu a fost încă creat?

Nu, weak poate indica doar către un obiect existent sau nil. La crearea unui obiect nou obținem mai întâi o referință strong (prin inițializator), și doar apoi putem atribui o referință slabă. weak nil la început — o stare normală.

De ce weak funcționează doar cu class-types?

Weak se bazează pe ARC, care gestionează doar tipurile de referință (clase). Tipurile de valoare (struct, enum) se copiază la atribuire și nu au retain count. Pentru legătura slabă a tipurilor de valoare folosește closure-uri sau wrapper în class cu proprietate weak.

Cum afectează weak performanța într-o buclă?

Fiecare acces la o referință weak execută un lookup în tabela runtime. Într-o buclă cu milioane de iterații acesta poate fi de 2–5 ori mai lent decât o referință strong. Pentru căi fierbinți copiază weak într-o variabilă strong locală înainte de buclă.

Când poate o referință weak să devină nil neașteptat?

Când toate referințele strong către obiect sunt pierdute — la sfârșitul scopului, la resetarea proprietății, la închiderea ecranului. În mediul multi-thread aceasta se poate întâmpla între două linii de cod. Verifică întotdeauna weak prin guard let sau if let.

Cu ce se deosebește weak de __weak în Objective-C?

Semantic identic: ambele oferă zeroing weak. Diferențe: Swift necesită tip optional și var, Objective-C folosește modificatorul property. Objective-C suportă de asemenea __unsafe_unretained — referință slabă fără zeroing (risc de pointer dangling).

Rezumat

  • Weak Reference — referință neposesivă care nu crește retain count-ul și se anulează automat la dezalocare
  • Sintaxaweak var + tip optional; doar class-types și protocoale AnyObject
  • Zeroing weak — runtime anulează toate referințele weak către obiectul eliberat, prevenind pointerii dangling
  • Scenarii — delegați, parent-child cu feedback, closure-uri asincrone ([weak self])
  • Weak vs Unowned — weak se anulează (sigur), unowned nu se anulează (risc de crash, dar non-optional)
  • Performanță — weak mai lent decât strong din cauza lookup-ului în tabela runtime; pentru căi fierbinți copiază în strong
  • Recomandare — dacă nu ești sigur de garanțiile de viață — alege weak

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și