Automatic Reference Counting (ARC) — систем управљања меморијом у Swift и Objective-C који аутоматски броји број референци за сваки објекат и ослобађа га када бројач достигне нулу. Према Apple Swift Documentation, 2026, ARC је уграђен у компилер и ради у фази компиљације, умећући позиве retain/release на одговарајућим местима. За разлику од Garbage Collection, 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 грешака.
Сваки објекат у меморији има бројач референци (retain count). При креирању објекта, бројач се поставља на 1. Када нова strong-референца указује на објекат — бројач се повећава (retain). Када strong-референца нестаје — бројач се смањује (release). По достизању нуле, објекат се одмах деалоцира.
Swift компилер не умеће retain/release при сваком додељивању — користи статичку анализу за оптимизацију. На примјер, ако је објекат гарантовано некориштен након преноса, компилер може да изостави сувишни release/retain. Ова оптимизација се назива ARC Optimisation.
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 и 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 ослобађа од ових брига, али по цени недетерминистичких пауза и додатне нити.
ARC дефинише три типа квалификатора референци, од којих сваки на другачији начин утиче на бројач и животни циклус објекта. Правилан одабир квалификатора је основа безбедног рада са меморијом у Swift-у.
Strong — подразумевани квалификатор. Свака strong-референца повећава retain count објекта за 1. Док постоји бар једна strong-референца, објекат је жив. Сва својства класа и локалне промењиве у Swift-у су подразумевано strong. Strong-референце стварају однос власништва: објекат A поседује објекат B.
Weak — референца која не повећава retain count. Објекат може бити деалоциран чак и ако на њега указује weak-референца. Након деалокације, weak-референца се аутоматски поставља на nil. Weak-референце се увек декларишу као var са опционалним типом (?). Користе се за разбијање retain cycle-а, посебно у delegate обрасцу.
Unowned — невласничка референца која, као и weak, не повећава retain count. Међутим, unowned-референца се не поставља на nil након деалокације — приступ ослобођеном објекту изазива crash. Unowned се користи када је гарантовано да објекат живи бар колико и објекат који га реферира. Типичан сценаријо — затворења (closures) и односи родитељ-дете са гарантованим веком.
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 није панацеја. Развојачи се суочавају са неколико типичних проблема који захтевају разумевање унутрашњег механизма управљања меморијом.
Затворења (closures) у Swift-у хватају спољашње промењиве путем strong-референце. Ако је затворење додијељено својству класе и хвата self — настаје retain cycle: класа држи затворење, затворење држи self. Решење — capture list са weak или unowned.
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 лаке операције, у врућим петљама чести инкременти/декременти бројача стварају додатни трошак. У Swift 5.9+, компилер користи оптимизацију, при којој се сувишни retain/release уклањају ако анализатор докаже безбедност. Ипак, у Objective-C-у retain/release и даље могу бити уско грло у сценаријима са високим оптерећењем са милионима позива у секунди.
Autorelease Pool — механизам одложеног release-а, који се користи у Objective-C-у и неким сценаријима Swift-а. Објекти се смештају у пул и добијају release при drain-у пула. У петљама са великим бројем привремених објеката (на примјер, парсирање JSON-а) креирање сопственог autoreleasepool-а смањује пикну потрошњу радне меморије.
Често постављана питања
Код ручног управљања (MRR), развојач је експлицитно позивао retain, release и autorelease. ARC аутоматски умеће ове позиве у фази компиљације, елиминишући ризик double-free-а, цурења због заборављеног release-а и грешака балансирања retain/release.
ARC управља само Objective-C објектима и Swift class-овима. За C/C++ структуре и показиве, ARC се не примењује — ови објекти се управљају ручно или путем C++ паметних показивача (shared_ptr, unique_ptr). Core Foundation објекти (CFString, CGColor) такође нису под ARC-ом.
weak — када објекат може бити деалоциран прије објекта који га реферира (delegati, асинхроне затворења). unowned — када је гарантовано да објекат живи бар колико и објекат који га реферира (родитељ-дете, где дете не може да постоји без родитеља). Ако нисте сигурни — изаберите weak.
Егзистенцијални типови (protocol as type) у Swift-у пакују вредност у специјални контејнер (existential container). То повећава број retain/release на границама протокола. У Swift 5.7+, opaque result types и some параметри смањују додатни трошак елиминацијом контејнера.
Нема директног API-ја за читање retain count-а у Swift-у — то се сматра детаљом имплементације. За дијагностику користите Instruments (Allocations, Leaks) или Memory Debugger у Xcode-у. Ови алати показују број живих инстанци класе и ланце задржавања.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође