Strong Reference (јакa референца): шта је то, механизам рада и ARC

Аутор: IT Sectr Објављено: 2026-03-30 Време читања: 9 мин

Strong Reference (јакa референца) — то је стандардни механизам управљања меморијом при којем објекат остаје у меморији док год на њега показује бар једна активна референца. За разлику од слабих референци, јака референца повећава бројач референци објекта и спречава његово аутоматско ослобађање. Према Apple Developer Documentation, ARC аутоматски управља животним веком објеката у Swift-у и Objective-C-у. Разумевање рада јаких референци је кључно за спречавање цурења меморије и цикличних зависности у мобилним апликацијама.

Главно

  • Strong Reference — референца која држи објекат у меморији, повећавајући његов retain count за 1.
  • ARC аутоматски убацује операције release и retain, елиминишући ручно управљање меморијом у Swift-у и Objective-C-у.
  • Retain cycle настаје када два објекта упућују један на другог преко јаких референци — меморија се никада не ослобађа.
  • Weak Reference не повећава бројач референци и аутоматски се поништава при ослобађању објекта.
  • Unowned Reference не повећава бројач, али претпоставља да објекат не живи дуже од власника.

Шта је Strong Reference?

Strong Reference — то је тип референце на објекат који спречава његово уништење од стране сакупљача отпада или система за управљање меморијом. Док год постоји бар једна јака референца на објекат, меморија под њим се не ослобађа. Ово је основни механизам на коме су изграђени ARC у Swift-у и Objective-C-у и сакупљање отпада у Java-и и Kotlin-у.

Концепт јаке референце је фундаменталан за све језике са аутоматским управљањем меморијом. У системима са ARC-ом свака јака референца повећава бројач референци објекта. Када бројач падне на нулу, објекат се одмах дистанцира. У Java-и и Kotlin-у са сакупљачем отпада јака референца гарантује да је објекат достижан и да неће бити сакупљен од стране GC-а.

Према подацима WWDC 2021, око 35% цурења меморије у iOS апликацијама је повезано са неправилном употребом јаких референци и циклусима задржавања. У Android развоју, цурења кроз имплицитне strong reference у затварањима и повратним позивима су други најчешћи узрок проблема са меморијом после Context Leak-а.

За ефикасан рад са меморијом потребно је разумети разлику између strong, weak и unowned референци и правилно одабрати тип референце у зависности од власништва и животног века објеката.

Како је ARC променио приступ управљању меморијом

Пре увођења ARC-а, програмери су ручно позивали retain и release за сваки објекат, што је доводило до бројних грешака. ARC, који је Apple представио 2011. године са издањем LLVM 3.0, аутоматизовао је овај процес анализирајући граф власништва у фази компилације. Компилатор сам убацује позиве retain, release и autorelease на одговарајућа места.

Према Clang Static Analyzer-у, увођење ARC-а смањило је број багова везаних за меморију у iOS апликацијама за 70%. За програмера то значи да је управљање меморијом постало сигурније, али истовремено се појавила потреба да се разуме како јаке референце раде испод хаубе — да би се избегли retain cycles.

У Kotlin-у и Java-и улогу ARC-а игра сакупљач отпада, али принцип јаке референце остаје исти: GC Roots — то су улазне тачке кроз које се објекти држе јаким референцама. Док год је објекат достижан кроз ланац јаких референци од GC Root-а, он неће бити сакупљен.

Како ради Strong Reference у ARC-у?

ARC (Automatic Reference Counting) ради на принципу бројања референци за сваки објекат на гомили. Када се креира нова јака референца на објекат, бројач се повећава (retain). Када се референца уништи или препише, бројач се смањује (release). Када бројач достигне нулу, објекат се одмах уклања из меморије.

Размотримо пример у Swift-у. Приликом креирања инстанце класе, ARC додељује меморију и поставља retain count на 1. Свако ново додељивање другој променљивој повећава бројач. Када променљива изађе из видокруга, бројач се смањује:

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

    func loadProfile() {
        // retain count = 1 за нову инстанцу
        let user = User(name: "Ivan")
        // retain count = 2 након додељивања nameLabel
        nameLabel = user.name
        // излаз из методе — user излази из опсега, retain count = 1
    }
}

У овом коду ARC гарантује да објекат User остаје у меморији док год постоји бар једна јака референца на њега. Када функција loadProfile заврши, локална променљива user се уништава, али nameLabel и даље држи објекат. Меморија ће се ослободити тек када nameLabel престане да постоји или буде преписана.

У Kotlin-у слично понашање се обезбеђује кроз GC Roots. Док год постоји следљив ланац strong references од корена сакупљача отпада (на пример, статичко поље или активна нит), објекат остаје у меморији. Разлика је у томе што GC не ослобађа меморију тренутно — то се дешава асинхроно након анализе достижности.

Када долази до ослобађања меморије

У ARC-у ослобађање се дешава синхроно у тренутку постављања бројача на нулу. У Swift-у и Objective-C-у тачно знате када ће објекат бити уклоњен. У Kotlin-у и Java-и тренутак ослобађања је непредвидив, али то се компензује флексибилнијом шемом откривања цикличних зависности на нивоу сакупљача отпада.

Retain Cycles и цурење меморије

Retain cycle (циклус задржавања) — ситуација у којој два или више објеката имају узајамне јаке референце један на другог. Као резултат, њихов retain count никада не пада на нулу, а меморија се не ослобађа чак ни након што објекти престану да буду потребни апликацији.

Класичан пример: родитељски view controller држи подређени објекат јаком референцом, а он заузврат јаком референцом држи родитеља. Ово је типично за ситуације са делегатима, затварањима и угнежђеним ламбда изразима. Према Instruments Leaks-у, retain cycles чине до 60% свих цурења меморије у апликацијама које користе ARC.

swift
class ParentViewController: UIViewController {
    var child: ChildViewController?

    func setupChild() {
        child = ChildViewController()
        // retain cycle: parent држи child, child држи parent кроз closure
        child?.onEvent = {
            self.handleEvent()
        }
    }

    func handleEvent() {}
}

Проблем је у томе што затварање onEvent хвата self (ParentViewController) јаком референцом, а сам ParentViewController држи child јаком референцом. Оба објекта никада неће бити ослобођена. Решење — користити weak self у затварању да би се прекинуо циклус.

У Kotlin-у слични циклуси настају при коришћењу ламбди које хватају спољашње објекте. JVM сакупљач отпада може временом открити такве циклусе, али само ако су објекти недостижни из GC Roots-а. Ако је циклус повезан са активном нити или UI контекстом, цурење остаје током целог животног века апликације.

Strong vs Weak vs Unowned Reference

Разумевање разлике између типова референци — кључ за безбедно управљање меморијом. Strong Reference повећава retain count. Weak Reference не повећава retain count и аутоматски постаје nil при ослобађању објекта. Unowned Reference такође не повећава retain count, али се не поништава — позивање на њега након ослобађања изазива пад.

Тип референцеRetain countСигурностКада користити
Strong+1Сигурно (подразумевано)Власништво над објектом, однос parent → child
WeakНе мењаАутоматско поништавање (safe)Делегати, callback, повратне референце
UnownedНе мењаРизик од пада при каснијем позивањуКада објекат живи гарантовано дуже од власника

Избор типа референце диктира однос власништва. Ако је објекат B део A и не може постојати без њега — користите Strong. Ако B може постојати независно и упућује на A ради обавештења — користите Weak. Unowned се ретко примењује — само када животни век подређеног објекта строго не прелази животни век родитеља.

Практично правило избора

Apple Developer Documentation препоручује: подразумевано користите strong за све односе власништва. Ако је потребно избећи retain cycle — одредите која референца треба да буде слаба. Обично је то повратна референца у хијерархији (child → parent). У Kotlin-у сличну улогу има WeakReference из java.lang.ref, који се примењује за кешеве и шаблоне посматрача.

Како поправити проблеме са јаким референцама

Откривање retain cycles — први корак. Други — правилно их отклањање. Главно средство борбе против циклуса јаких референци је замена једне од референци са weak или unowned. У језицима са сакупљањем отпада додатно се примењује WeakReference са ручном провером на null пре сваког приступа.

У Swift-у и Objective-C-у најчешћа исправка је додавање [weak self] у затварања. Ово гарантује да затварање не држи објекат након његовог ослобађања. У Kotlin-у за сличне сврхе се користи WeakReference омотач или експлицитно чишћење референце у onDestroy-у.

swift
class NetworkService {
    func fetchData(completion: @escaping (Data?) -> Void) {
        // хватање кроз weak self — retain cycle искључен
        URLSession.shared.dataTask(
            with: URL(string: "https://api.example.com")!
        ) { [weak self] data, response, error in
            guard let self else { return }
            completion(data)
        }.resume()
    }
}

У овом примеру [weak self] гарантује да NetworkService неће бити држан од стране затварања након што више није потребан. Ако је self ослобођен пре завршетка захтева — guard let self else { return } излази из затварања без позивања completion-а.

За дијагностику retain cycles користите Instruments Leaks за iOS или Android Profiler + LeakCanary за Android. Ови алати показују тачан граф задржавања и указују која јака референца спречава ослобађање објекта. Редовно профилисање меморије треба да буде део CI/CD пајплајна сваког мобилног пројекта.

Strong Reference у Swift-у и Kotlin-у — поређење

Swift и Kotlin користе суштински различите механизме управљања меморијом, али концепт јаке референце присутан је у оба. У Swift-у се примењује ARC са синхроним ослобађањем при retain count = 0. У Kotlin-у се користи трасирајући GC који асинхроно чисти недостижне објекте.

ПараметарSwift (ARC)Kotlin (JVM GC)
МеханизамБројање референци (retain count)Праћење достижности (GC Roots)
ОслобађањеСинхроно (при постављању бројача на нулу)Асинхроно (по циклусу GC)
Retain cycleНе открива се аутоматскиGC може открити, али не одмах
Weak refweak (аутоматско поништавање)WeakReference (ручна провера)

Главна практична разлика: у Swift-у retain cycle — то је гарантовано цурење. У Kotlin-у GC може прекинути циклус ако су објекти недостижни из корена, али животни век процурелих објеката остаје непредвидив. Због тога је у оба језика најбоља стратегија избегавати циклусе јаких референци у фази пројектовања.

За Swift користите weak у делегатским обрасцима и затварањима. За Kotlin — WeakReference или Lifecycle-aware компоненте које аутоматски чисте референце при уништењу власника. У оба приступа циљ је исти — искључити јаке референце тамо где стварају нераскидив ланац задржавања.

Често постављана питања

Че се Strong Reference разликује од Weak Reference?

Strong Reference повећава retain count објекта и спречава његово ослобађање док год референца постоји. Weak Reference не мења retain count и аутоматски се поништава када се објекат уклони из меморије. Јаке референце се користе за власништво, слабе — за повратне везе и делегате.

Шта је retain cycle и зашто је опасан?

Retain cycle — међусобна блокада у којој два објекта држе један другог јаким референцама. Њихов retain count никада не пада на нулу, меморија се не ослобађа. То доводи до цурења меморије: објекти остају на гомили заувек, апликација троши све више ресурса и на крају пада са OutOfMemory.

Како открити retain cycle у iOS апликацији?

Користите Instruments Leaks из Xcode-а — покрените профилисање са шаблоном Leaks, извршите сценарио у апликацији и проверите индикаторе цурења. За прецизну дијагностику пређите на картицу Cycles & Roots — она ће приказати граф узајамних strong reference које формирају нераскидив циклус.

Када треба користити Unowned уместо Weak?

Unowned примењујте када животни век подређеног објекта сигурно не прелази животни век родитеља — на пример, при повезивању објекта са строго дефинисаним опсегом. Ако постоје сумње, користите Weak, јер позивање на ослобођену unowned референцу изазива пад апликације.

Да ли јаке референце утичу на перформансе апликације?

Индиректно — да. Сваки retain и release у ARC-у је атомска операција са додатним трошковима. При великом броју објеката у циклусима то може утицати на перформансе. Међутим, главни проблем није брзина рада ARC-а, већ цурење меморије због неправилно изабраног типа референце.

Закључак

  • Strong Reference — основни механизам власништва над објектом, који га држи у меморији кроз повећање retain count-а.
  • ARC аутоматизује управљање меморијом у Swift-у и Objective-C-у, елиминишући ручни retain и release, али не штити од retain cycles.
  • Retain cycle настаје при узајамним јаким референцама — то је главни узрок цурења меморије у ARC системима.
  • Weak и Unowned референце прекидају циклусе јаких референци, не повећавајући retain count.
  • Избор типа референце одређује однос власништва: Strong за parent→child, Weak или Unowned за child→parent.
  • Instruments Leaks и LeakCanary — главни алати за откривање проблематичних јаких референци у iOS-у и Android-у.
  • Пројектујте граф власништва унапред — то је јефтиније него поправљати цурење меморије након објављивања апликације.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође