ARC: шта је, принцип рада Automatic Reference Counting у iOS-у

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

Automatic Reference Counting (ARC) — систем управљања меморијом у Swift и Objective-C који аутоматски броји број референци за сваки објекат и ослобађа га када бројач достигне нулу. Према Apple Swift Documentation, 2026, ARC је уграђен у компилер и ради у фази компиљације, умећући позиве retain/release на одговарајућим местима. За разлику од Garbage Collection, ARC не захтева посебну нит сакупљача и не ствара паузе током извршавања апликације.

Главно

  • ARC — Automatic Reference Counting, компилерски систем управљања меморијом у Swift и Objective-C
  • Принцип рада — сваки објекат има бројач референци (retain count), када се поштишти објекат се одмах ослабађа
  • Квалификатори — strong, weak и unowned одређују како референца утиче на бројач и животни циклус објекта
  • Разлика од GC — ARC ради детерминистички у фази компиљације, без Stop-The-World пауза и позадинске нити сакупљача
  • Retain Cycle — главни проблем ARC-а: ако се два објекта позивају један на други путем strong, њихов бројач никада не достиже нулу

Шта је ARC?

ARC (Automatic Reference Counting) — је механизам компилерског управљања меморијом, који је представио Apple у Xcode 4.2 (2011) за Objective-C и наследио Swift. За разлику од ручног управљања меморијом (Manual Retain-Release, MRR), ARC потпуно аутоматизује позиве retain, release и autorelease, умећући их у фази компиљације без учешћа развојача.

ARC није сакупљач отпада. То је статичка анализа са динамичким убацивањем кода: компилер анализира век објеката и поставља retain/release у тачкама где се објекти креирају, копирају или излазе из области видљивости. Резултат — детерминистичко ослабађање меморије: објекат се брише тачно у тренутку када нестаје да постоји референца на њега, без кашњења и пауза.

Према WWDC 2011 Session 323, прелазак са MRR-а на ARC смањио је број crash-багова везаних за меморију за 70% у Апловим апликацијама. Развојачи су престали ручно балансирати retain/release, што је елиминисало целу класу цурења и double-free грешака.

Како ради Automatic Reference Counting

Сваки објекат у меморији има бројач референци (retain count). При креирању објекта, бројач се поставља на 1. Када нова strong-референца указује на објекат — бројач се повећава (retain). Када strong-референца нестаје — бројач се смањује (release). По достизању нуле, објекат се одмах деалоцира.

Swift компилер не умеће retain/release при сваком додељивању — користи статичку анализу за оптимизацију. На примјер, ако је објекат гарантовано некориштен након преноса, компилер може да изостави сувишни release/retain. Ова оптимизација се назива ARC Optimisation.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) иницијализован (retain count: 1)")
    }
    deinit {
        print("\(name) деалоциран")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q излази из области
    // retain count = 1
    // p излази из области
    // retain count = 0 → deinit
}

У овом примјеру се види како ARC управља бројачем: при додељивању q = p бројач се повећава, при изласку q-а из области — смањује. Када последња strong-референца нестане, деиницијализатор се позива одмах. Ниједан сакупљач отпада не чека — меморија се ослабађа одмах.

ARC напрама GC: кључне разлике

ARC и Garbage Collection решавају исти задатак — аутоматско управљање меморијом — али суштински различитим приступима. Избор између њих одређује архитектуру језика: Swift (ARC) напрама Java/Go (GC). Размотримо основне разлике.

КарактеристикаARC (Swift/ObjC)GC (Java/Go)
Тренутак ослабађањаДетерминистички: одмах по поштиштању бројачаНедетерминистички: при следећој збирци
Паузе извршавањаНема (умеци retain/release у фази компиљације)Има Stop-The-World паузе (2–200 мс)
Додатни трошакИнкремент/декремент бројача при свакој референциОбилазак графа објеката, означавање, ослабађање
ПроблемиRetain Cycle (ручно решавање)Фрагментација хипа, цурење код заборављених референци
Додатна нитНије потребнаПотребна је нит сакупљача отпада

Кључни компромис: ARC пружа предвидив век објеката и нулте паузе, али захтева од развојача разумевање retain cycle-а и правилан одабир weak/unowned. GC ослобађа од ових брига, али по цени недетерминистичких пауза и додатне нити.

Strong, Weak и Unowned: квалификатори референци у ARC-у

ARC дефинише три типа квалификатора референци, од којих сваки на другачији начин утиче на бројач и животни циклус објекта. Правилан одабир квалификатора је основа безбедног рада са меморијом у Swift-у.

Strong

Strong — подразумевани квалификатор. Свака strong-референца повећава retain count објекта за 1. Док постоји бар једна strong-референца, објекат је жив. Сва својства класа и локалне промењиве у Swift-у су подразумевано strong. Strong-референце стварају однос власништва: објекат A поседује објекат B.

Weak

Weak — референца која не повећава retain count. Објекат може бити деалоциран чак и ако на њега указује weak-референца. Након деалокације, weak-референца се аутоматски поставља на nil. Weak-референце се увек декларишу као var са опционалним типом (?). Користе се за разбијање retain cycle-а, посебно у delegate обрасцу.

Unowned

Unowned — невласничка референца која, као и weak, не повећава retain count. Међутим, unowned-референца се не поставља на nil након деалокације — приступ ослобођеном објекту изазива crash. Unowned се користи када је гарантовано да објекат живи бар колико и објекат који га реферира. Типичан сценаријо — затворења (closures) и односи родитељ-дете са гарантованим веком.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) деалоциран") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — не влада
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Картица \(number) деалоцирана") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer и CreditCard обоје ослобођени — нема retain cycle

Овде CreditCard користи unowned-референцу на Customer. Customer поседује картицу (strong), а картица не поседује клијента (unowned). Када Customer постане nil, оба објекта се ослабађају — retain cycle се не јавља. Да је card.customer био strong, циклус би блокирао ослабађање.

Типични проблеми ARC-а и њихово решење

Упркос аутоматизацији, ARC није панацеја. Развојачи се суочавају са неколико типичних проблема који захтевају разумевање унутрашњег механизма управљања меморијом.

Retain Cycle у затворењима

Затворења (closures) у Swift-у хватају спољашње промењиве путем strong-референце. Ако је затворење додијељено својству класе и хвата self — настаје retain cycle: класа држи затворење, затворење држи self. Решење — capture list са weak или unowned.

swift
class NetworkManager {
    var completionHandler: ((Data?) -> Void)?
    var data: Data?

    func fetchData() {
        completionHandler = { [weak self] result in
            guard let self else { return }
            self.data = result
            self.processResult()
        }
    }

    func processResult() { }
}

Capture list [weak self] ствара weak-референцу на self унутар затворења. Ово разбија потенцијални retain cycle. Guard let self гарантује да је објекат жив пре извршавања кода. weak self — стандардна пракса за асинхроне затворења у Swift-у.

Перформансе retain/release

Иако су retain/release лаке операције, у врућим петљама чести инкременти/декременти бројача стварају додатни трошак. У Swift 5.9+, компилер користи оптимизацију, при којој се сувишни retain/release уклањају ако анализатор докаже безбедност. Ипак, у Objective-C-у retain/release и даље могу бити уско грло у сценаријима са високим оптерећењем са милионима позива у секунди.

Autorelease Pool

Autorelease Pool — механизам одложеног release-а, који се користи у Objective-C-у и неким сценаријима Swift-а. Објекти се смештају у пул и добијају release при drain-у пула. У петљама са великим бројем привремених објеката (на примјер, парсирање JSON-а) креирање сопственог autoreleasepool-а смањује пикну потрошњу радне меморије.

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

По чему се ARC разликује од ручног управљања меморијом (MRR)?

Код ручног управљања (MRR), развојач је експлицитно позивао retain, release и autorelease. ARC аутоматски умеће ове позиве у фази компиљације, елиминишући ризик double-free-а, цурења због заборављеног release-а и грешака балансирања retain/release.

Може ли ARC да ради са C/C++ кодом?

ARC управља само Objective-C објектима и Swift class-овима. За C/C++ структуре и показиве, ARC се не примењује — ови објекти се управљају ручно или путем C++ паметних показивача (shared_ptr, unique_ptr). Core Foundation објекти (CFString, CGColor) такође нису под ARC-ом.

Када користити weak, а када unowned?

weak — када објекат може бити деалоциран прије објекта који га реферира (delegati, асинхроне затворења). unowned — када је гарантовано да објекат живи бар колико и објекат који га реферира (родитељ-дете, где дете не може да постоји без родитеља). Ако нисте сигурни — изаберите weak.

Шта су егзистенцијални типови и како утичу на ARC?

Егзистенцијални типови (protocol as type) у Swift-у пакују вредност у специјални контејнер (existential container). То повећава број retain/release на границама протокола. У Swift 5.7+, opaque result types и some параметри смањују додатни трошак елиминацијом контејнера.

Како проверити retain count у Swift-у?

Нема директног API-ја за читање retain count-а у Swift-у — то се сматра детаљом имплементације. За дијагностику користите Instruments (Allocations, Leaks) или Memory Debugger у Xcode-у. Ови алати показују број живих инстанци класе и ланце задржавања.

Резиме

  • ARC — компилерски систем управљања меморијом Swift-а и Objective-C-а који ради кроз бројање референци
  • Принцип — сваки објекат има retain count; при поштиштању објекат се одмах и детерминистички ослабађа
  • Разлика од GC — ARC ради без позадинске нити и Stop-The-World пауза, али захтева контролу retain cycle-а
  • Strong — повећава бројач; weak и unowned — не повећавају, али unowned се не брише при деалокацији
  • Затворења — главни узрок retain cycle-а у Swift-у; capture list [weak self] — стандардно решење
  • Autorelease Pool — механизам одложеног ослабађања за привремене објекте у петљама и прилагођеним сценаријима
  • Дијагностика — Xcode Memory Debugger, Instruments и LeakCanary (путем ObjC bridge) за проналажење проблема

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

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

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

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