Strong Reference (referință puternică): ce este, mecanismul de funcționare și ARC

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

Strong Reference (referință puternică) — este un mecanism standard de gestionare a memoriei în care un obiect rămâne în memorie atâta timp cât există cel puțin o referință activă către el. Spre deosebire de referințele slabe, referința puternică incrementează contorul de referințe al obiectului și previne eliberarea sa automată. Conform Apple Developer Documentation, ARC gestionează automat durata de viață a obiectelor în Swift și Objective-C. Înțelegerea funcționării referințelor puternice este critică pentru prevenirea scurgerilor de memorie și a dependențelor ciclice în aplicațiile mobile.

Principalele idei

  • Strong Reference — referință care menține obiectul în memorie, incrementându-i retain count cu 1.
  • ARC inserează automat operațiile release și retain, eliminând gestionarea manuală a memoriei în Swift și Objective-C.
  • Retain cycle apare când două obiecte se referă unul la altul prin referințe puternice — memoria nu este eliberată niciodată.
  • Weak Reference nu incrementează contorul de referințe și se anulează automat la eliberarea obiectului.
  • Unowned Reference nu incrementează contorul, dar presupune că obiectul nu trăiește mai mult decât proprietarul.

Ce este Strong Reference?

Strong Reference — este un tip de referință către un obiect care previne distrugerea acestuia de către colectorul de gunoi sau sistemul de gestionare a memoriei. Atâta timp cât există cel puțin o referință puternică către obiect, memoria acestuia nu este eliberată. Acesta este mecanismul de bază pe care se bazează ARC în Swift și Objective-C și garbage collection în Java și Kotlin.

Conceptul de referință puternică este fundamental pentru toate limbajele cu gestionare automată a memoriei. În sistemele cu ARC, fiecare referință puternică incrementează contorul de referințe al obiectului. Când contorul ajunge la zero, obiectul este dealocat imediat. În Java și Kotlin cu garbage collector, referința puternică garantează că obiectul este accesibil și nu va fi colectat de GC.

Conform datelor WWDC 2021, aproximativ 35% din scurgerile de memorie în aplicațiile iOS sunt legate de utilizarea incorectă a referințelor puternice și de ciclurile de reținere. În dezvoltarea Android, scurgerile prin strong reference implicite în closure-uri și callback-uri reprezintă a doua cea mai frecventă cauză a problemelor de memorie după Context Leak.

Pentru a lucra eficient cu memoria, este necesar să înțelegem diferența dintre referințele strong, weak și unowned și să alegem corect tipul de referință în funcție de posesie și durata de viață a obiectelor.

Cum a schimbat ARC abordarea gestionării memoriei

Înainte de introducerea ARC, dezvoltatorii apelau manual retain și release pentru fiecare obiect, ceea ce ducea la numeroase erori. ARC, introdus de Apple în 2011 odată cu lansarea LLVM 3.0, a automatizat acest proces analizând graful de posesie în faza de compilare. Compilatorul însuși inserează apelurile retain, release și autorelease în locurile potrivite.

Conform Clang Static Analyzer, introducerea ARC a redus numărul de bug-uri legate de memorie în aplicațiile iOS cu 70%. Pentru dezvoltator, aceasta înseamnă că gestionarea memoriei a devenit mai sigură, dar simultan a apărut necesitatea de a înțelege cum funcționează referințele puternice sub capotă — pentru a evita retain cycles.

În Kotlin și Java, rolul ARC este îndeplinit de garbage collector, dar principiul referinței puternice rămâne același: GC Roots — sunt punctele de intrare prin care obiectele sunt menținute prin referințe puternice. Atâta timp cât un obiect este accesibil printr-un lanț de referințe puternice de la GC Root, el nu va fi colectat.

Cum funcționează Strong Reference în ARC?

ARC (Automatic Reference Counting) funcționează pe principiul numărării referințelor pentru fiecare obiect din heap. Când se creează o nouă referință puternică către un obiect, contorul crește (retain). Când referința este distrusă sau suprascrisă, contorul scade (release). Când contorul ajunge la zero, obiectul este imediat șters din memorie.

Să considerăm un exemplu în Swift. La crearea unei instanțe a unei clase, ARC alocă memorie și setează retain count la 1. Fiecare nouă atribuire unei alte variabile incrementează contorul. Când variabila iese din domeniul de vizibilitate, contorul scade:

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

    func loadProfile() {
        // retain count = 1 pentru o instanță nouă
        let user = User(name: "Ivan")
        // retain count = 2 după atribuirea nameLabel
        nameLabel = user.name
        // ieșirea din metodă — user iese din domeniu, retain count = 1
    }
}

În acest cod, ARC garantează că obiectul User rămâne în memorie atâta timp cât există cel puțin o referință puternică către el. Când funcția loadProfile se termină, variabila locală user este distrusă, dar nameLabel încă menține obiectul. Memoria va fi eliberată doar când nameLabel încetează să existe sau este suprascrisă.

În Kotlin, un comportament similar este asigurat prin GC Roots. Atâta timp cât există un lanț trasabil de strong references de la rădăcina colectorului de gunoi (de exemplu, un câmp static sau un thread activ), obiectul rămâne în memorie. Diferența este că GC nu eliberează memoria instantaneu — aceasta se întâmplă asincron după analiza accesibilității.

Când are loc eliberarea memoriei

În ARC, eliberarea are loc sincron în momentul zeroirii contorului. În Swift și Objective-C, știi exact când obiectul va fi șters. În Kotlin și Java, momentul eliberării este imprevizibil, dar acest lucru este compensat de o schemă mai flexibilă de detectare a dependențelor ciclice la nivelul garbage collector-ului.

Retain Cycles și scurgeri de memorie

Retain cycle (ciciu de reținere) — situația în care două sau mai multe obiecte au referințe puternice reciproce între ele. Ca rezultat, retain count al lor nu scade niciodată la zero, iar memoria nu este eliberată chiar și după ce obiectele nu mai sunt necesare aplicației.

Exemplu clasic: un view controller părinte menține un obiect copil cu o referință puternică, iar acesta la rândul său menține părintele cu o referință puternică. Acest lucru este tipic pentru situațiile cu delegați, closure-uri și expresii lambda imbricate. Conform Instruments Leaks, retain cycles constituie până la 60% din toate scurgerile de memorie în aplicațiile care utilizează ARC.

swift
class ParentViewController: UIViewController {
    var child: ChildViewController?

    func setupChild() {
        child = ChildViewController()
        // retain cycle: parent menține child, child menține parent prin closure
        child?.onEvent = {
            self.handleEvent()
        }
    }

    func handleEvent() {}
}

Problema aici este că closure-ul onEvent capturează self (ParentViewController) cu o referință puternică, iar ParentViewController însuși menține child cu o referință puternică. Ambele obiecte nu vor fi niciodată eliberate. Soluția — utilizarea weak self în closure pentru a rupe ciclul.

În Kotlin, cicluri similare apar la utilizarea lambda-urilor care capturează obiecte externe. Garbage collector-ul JVM poate detecta în timp astfel de cicluri, dar numai dacă obiectele sunt inaccesibile din GC Roots. Dacă ciclul este legat de un thread activ sau un context UI, scurgerea rămâne pe toată durata de viață a aplicației.

Strong vs Weak vs Unowned Reference

Înțelegerea diferenței dintre tipurile de referințe — cheia gestionării sigure a memoriei. Strong Reference incrementează retain count. Weak Reference nu incrementează retain count și devine automat nil la eliberarea obiectului. Unowned Reference de asemenea nu incrementează retain count, dar nu se anulează — referirea la el după eliberare cauzează un crash.

Tip referințăRetain countSiguranțăCând să folosești
Strong+1Sigur (implicit)Posesia obiectului, relația parent → child
WeakNu modificăAuto-anulare (safe)Delegați, callback, referințe inverse
UnownedNu modificăRisc de crash la referirea târzieCând obiectul trăiește garantat mai mult decât proprietarul

Alegerea tipului de referință este dictată de relația de posesie. Dacă obiectul B este parte a lui A și nu poate exista fără el — folosește Strong. Dacă B poate exista independent și se referă la A pentru notificări — folosește Weak. Unowned se aplică rar — doar când durata de viață a obiectului copil nu depășește strict durata de viață a părintelui.

Regula practică de selecție

Apple Developer Documentation recomandă: implicit, folosește strong pentru toate relațiile de posesie. Dacă este necesar să eviți un retain cycle — stabilește care referință ar trebui să fie slabă. De obicei, aceasta este referința inversă în ierarhie (child → parent). În Kotlin, un rol similar îi revine WeakReference din java.lang.ref, care se aplică pentru cache-uri și pattern-ul observer.

Cum să remediem problemele cu referințe puternice

Detectarea retain cycles — primul pas. Al doilea — eliminarea lor corectă. Instrumentul principal de combatere a ciclurilor de referințe puternice este înlocuirea uneia dintre referințe cu weak sau unowned. În limbajele cu garbage collection, se aplică suplimentar WeakReference cu verificare manuală de null înainte de fiecare acces.

În Swift și Objective-C, cea mai frecventă remediere este adăugarea [weak self] în closure-uri. Aceasta garantează că closure-ul nu menține obiectul după eliberarea sa. În Kotlin, în scopuri similare se utilizează învelișul WeakReference sau curățarea explicită a referinței în onDestroy.

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

În acest exemplu, [weak self] garantează că NetworkService nu va fi menținut de closure după ce nu mai este necesar. Dacă self a fost eliberat înainte de finalizarea solicitării — guard let self else { return } iese din closure fără a apela completion.

Pentru diagnosticarea retain cycles, folosește Instruments Leaks pentru iOS sau Android Profiler + LeakCanary pentru Android. Aceste instrumente arată graful exact de reținere și indică care referință puternică împiedică eliberarea obiectului. Profilarea regulată a memoriei ar trebui să fie parte a pipeline-ului CI/CD al oricărui proiect mobil.

Strong Reference în Swift și Kotlin — comparație

Swift și Kotlin utilizează mecanisme fundamental diferite de gestionare a memoriei, dar conceptul de referință puternică este prezent în ambele. În Swift se aplică ARC cu eliberare sincronă la retain count = 0. În Kotlin se utilizează un GC de urmărire care curăță asincron obiectele inaccesibile.

ParametruSwift (ARC)Kotlin (JVM GC)
MecanismNumărarea referințelor (retain count)Urmărirea accesibilității (GC Roots)
EliberareSincronă (la zeroirea contorului)Asincronă (prin ciclul GC)
Retain cycleNu este detectat automatGC poate detecta, dar nu imediat
Weak refweak (auto-anulare)WeakReference (verificare manuală)

Principala diferență practică: în Swift, un retain cycle — este o scurgere garantată. În Kotlin, GC poate rupe ciclul dacă obiectele sunt inaccesibile din rădăcină, dar durata de viață a obiectelor scurse rămâne imprevizibilă. Prin urmare, în ambele limbaje, cea mai bună strategie este evitarea ciclurilor de referințe puternice în faza de proiectare.

Pentru Swift, folosește weak în pattern-urile delegate și closure-uri. Pentru Kotlin — WeakReference sau componente Lifecycle-aware care curăță automat referințele la distrugerea proprietarului. În ambele abordări, scopul este același — eliminarea referințelor puternice acolo unde creează un lanț de reținere de nesfârșit.

Întrebări frecvente

Cu ce se deosebește Strong Reference de Weak Reference?

Strong Reference incrementează retain count al obiectului și previne eliberarea sa atâta timp cât referința există. Weak Reference nu modifică retain count și se anulează automat când obiectul este șters din memorie. Referințele puternice sunt utilizate pentru posesie, cele slabe — pentru conexiuni inverse și delegați.

Ce este un retain cycle și de ce este periculos?

Retain cycle — o blocare reciprocă în care două obiecte se mențin unul pe altul cu referințe puternice. Retain count al lor nu scade niciodată la zero, memoria nu este eliberată. Aceasta duce la scurgeri de memorie: obiectele rămân în heap pentru totdeauna, aplicația consumă tot mai multe resurse și în cele din urmă crapă cu OutOfMemory.

Cum se detectează un retain cycle într-o aplicație iOS?

Folosește Instruments Leaks din Xcode — pornește profilarea cu șablonul Leaks, execută scenariul în aplicație și verifică indicatorii de scurgeri. Pentru o diagnosticare precisă, comută pe fila Cycles & Roots — aceasta va arăta graful referințelor puternice reciproce care formează un ciclu de nesfârșit.

Când trebuie folosit Unowned în loc de Weak?

Unowned se aplică atunci când durata de viață a obiectului copil cu siguranță nu depășește durata de viață a părintelui — de exemplu, la legarea unui obiect la un domeniu strict definit. Dacă există îndoieli, folosește Weak, deoarece referirea la un unowned eliberat cauzează un crash al aplicației.

Referințele puternice influențează performanța aplicației?

Indirect — da. Fiecare retain și release în ARC este o operație atomică cu costuri suplimentare. La un număr mare de obiecte în cicluri, aceasta poate afecta performanța. Cu toate acestea, problema principală nu este viteza de funcționare a ARC, ci scurgerile de memorie din cauza tipului de referință ales incorect.

Concluzii

  • Strong Reference — mecanismul de bază al posesiei obiectului, menținându-l în memorie prin incrementarea retain count.
  • ARC automatizează gestionarea memoriei în Swift și Objective-C, eliminând retain și release manuale, dar nu protejează de retain cycles.
  • Retain cycle apare la referințe puternice reciproce — aceasta este principala cauză a scurgerilor de memorie în sistemele ARC.
  • Referințele Weak și Unowned rup ciclurile de referințe puternice fără a incrementa retain count.
  • Alegerea tipului de referință este determinată de relația de posesie: Strong pentru parent→child, Weak sau Unowned pentru child→parent.
  • Instruments Leaks și LeakCanary — instrumentele principale pentru detectarea referințelor puternice problematice în iOS și Android.
  • Proiectează graful de posesie din timp — este mai ieftin decât remedierea scurgerilor de memorie după lansarea aplicației.

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