ARC: ano ito, prinsipyo ng paggana ng Automatic Reference Counting sa iOS

May-akda: IT Sectr Nai-publish: 2026-03-29 Oras ng pagbabasa: 8 min

Automatic Reference Counting (ARC) — sistema ng pamamahala ng memorya sa Swift at Objective-C na awtomatikong nagbibilang ng bilang ng mga referensiya sa bawat bagay at naglalabas nito kapag ang bilang ay umabot sa sero. Ayon sa Apple Swift Documentation, 2026, ang ARC ay naka-embed sa kompiler at gumagana sa yugto ng kompilasyon, naglalagay ng mga tawag na retain/release sa mga tamang lugar. Hindi tulad ng Garbage Collection, ang ARC ay hindi nangangailangan ng hiwalay na thread ng kolektor at hindi lumilikha ng mga pause habang isinasagawa ang aplikasyon.

Mga Pangunahing Punto

  • ARC — Automatic Reference Counting, sistema ng pamamahala ng memorya ng kompiler sa Swift at Objective-C
  • Prinsipyo ng paggana — bawat bagay ay may bilang ng referensiya (retain count), kapag naging sero ang bagay ay agad na pinakawalan
  • Mga Kwalipikador — strong, weak, at unowned ang nagtatakda kung paano naaapektuhan ng referensiya ang bilang at siklo ng buhay ng bagay
  • Pagkakaiba sa GC — ang ARC ay gumagana nang deterministiko sa yugto ng kompilasyon, walang mga Stop-The-World pause at background thread ng kolektor
  • Retain Cycle — pangunahing problema ng ARC: kung ang dalawang bagay ay nagre-refer sa isa’t isa sa pamamagitan ng strong, ang kanilang bilang ay hindi magiging sero

Ano ang ARC?

ARC (Automatic Reference Counting) — ay isang mekanismo ng pamamahala ng memorya sa antas ng kompiler, na ipinakilala ng Apple sa Xcode 4.2 (2011) para sa Objective-C at minana ng Swift. Hindi tulad ng manu-manong pamamahala ng memorya (Manual Retain-Release, MRR), ganap na ina-automate ng ARC ang mga tawag na retain, release, at autorelease, na inilalagay ang mga ito sa yugto ng kompilasyon nang walang partisipasyon ng developer.

Ang ARC ay hindi garbage collector. Ito ay isang static na pagsusuri na may dinamikong pagpasok ng code: sinusuri ng kompiler ang buhay ng mga bagay at inilalagay ang retain/release sa mga punto kung saan ang mga bagay ay nilikha, kinopya, o lumabas sa saklaw. Ang resulta — deterministikong pagpapakawala ng memorya: ang bagay ay tinatanggal eksakto sa sandaling wala nang referensiya dito, nang walang pagkaantala at pause.

Ayon sa WWDC 2011 Session 323, ang paglipat mula MRR patungong ARC ay nagbawas ng bilang ng crash-bug na may kaugnayan sa memorya ng 70% sa mga aplikasyon ng Apple. Ang mga developer ay tumigil sa manu-manong pag-balance ng retain/release, na nag-alis ng isang buong klase ng leak at double-free error.

Paano gumagana ang Automatic Reference Counting

Bawat bagay sa memorya ay may bilang ng referensiya (retain count). Sa paglikha ng bagay, ang bilang ay itinakda sa 1. Kapag ang isang bagong strong-referensiya ay tumuturo sa bagay — ang bilang ay tumataas (retain). Kapag nawala ang strong-referensiya — ang bilang ay bumababa (release). Sa pag-abot ng sero, ang bagay ay agad na deallocated.

Ang Swift kompiler ay hindi naglalagay ng retain/release sa bawat pagtatalaga — gumagamit ito ng static na pagsusuri para sa optimisasyon. Halimbawa, kung ang isang bagay ay garantisadong hindi gagamitin pagkatapos ng paglipat, maaaring laktawan ng kompiler ang sobrang release/retain. Ang optimisasyong ito ay tinatawag na ARC Optimisation.

swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) na-initialize (retain count: 1)")
    }
    deinit {
        print("\(name) na-deallocate")
    }
}

func testARC() {
    let p = Person(name: "Alice")  // retain count = 1
    let q = p                      // retain count = 2
    // q ay lumabas sa saklaw
    // retain count = 1
    // p ay lumabas sa saklaw
    // retain count = 0 → deinit
}

Sa halimbawang ito makikita kung paano pinamamahalaan ng ARC ang bilang: sa pagtatalaga q = p, ang bilang ay tumataas, sa paglabas ng q mula sa saklaw — bumababa. Kapag nawala ang huling strong-referensiya, ang deinitializer ay agad na tinatawag. Walang garbage collector na naghihintay — ang memorya ay agad na pinakawalan.

ARC vs Garbage Collection: mga pangunahing pagkakaiba

Ang ARC at Garbage Collection ay lumulutas ng parehong gawain — awtomatikong pamamahala ng memorya — ngunit may mga fundamental na magkaibang approach. Ang pagpili sa pagitan nila ay nagtatakda ng arkitektura ng wika: Swift (ARC) vs Java/Go (GC). Tingnan natin ang mga pangunahing pagkakaiba.

KatangianARC (Swift/ObjC)GC (Java/Go)
Sandali ng pagpapakawalaDeterministic: agad kapag sero ang bilangHindi deterministiko: sa susunod na koleksiyon
Mga pause sa pagpapatupadWala (mga pagpasok retain/release sa kompilasyon)May Stop-The-World pause (2–200 ms)
OverheadPagtaas/pagbaba ng bilang sa bawat referensiyaPagtawid sa graph ng bagay, pagmamarka, pagpapakawala
Mga problemaRetain Cycle (manu-manong paglutas)Fragmentasyon ng heap, leak sa nakalimutang referensiya
Karagdagang threadHindi kinakailanganKinakailangan ang thread ng garbage collector

Ang pangunahing kompromiso: ARC ay nagbibigay ng inaasahang buhay ng mga bagay at serong pause, ngunit nangangailangan ng pag-unawa sa retain cycle at tamang pagpili ng weak/unowned. Ang GC ay nagpapalaya mula sa mga alalahaning ito, ngunit sa halaga ng hindi deterministikong pause at karagdagang thread.

Strong, Weak, at Unowned: mga kwalipikador ng referensiya sa ARC

Ang ARC ay tumutukoy ng tatlong uri ng kwalipikador ng referensiya, bawat isa ay naiiba ang epekto sa bilang at siklo ng buhay ng bagay. Ang tamang pagpili ng kwalipikador ay pundasyon ng ligtas na pagtatrabaho sa memorya sa Swift.

Strong

Strong — ang default na kwalipikador. Bawat strong-referensiya ay nagtataas ng retain count ng bagay ng 1. Hangga’t mayroong kahit isang strong-referensiya, ang bagay ay buhay. Lahat ng property ng klase at lokal na variable sa Swift ay default na strong. Ang mga strong-referensiya ay lumilikha ng relasyon ng pagmamay-ari: bagay A ay nagmamay-ari ng bagay B.

Weak

Weak — referensiya na hindi nagtataas ng retain count. Ang bagay ay maaaring deallocated kahit na may weak-referensiya na tumuturo dito. Pagkatapos ng dealokasyon, ang weak-referensiya ay awtomatikong itinatakda sa nil. Ang weak-referensiya ay palaging idineklara bilang var na may opsyonal na uri (?). Ginagamit ito upang putulin ang retain cycle, lalo na sa pattern ng delegate.

Unowned

Unowned — hindi nagmamay-aring referensiya na, tulad ng weak, ay hindi nagtataas ng retain count. Gayunpaman, ang unowned-referensiya ay hindi itinatakda sa nil pagkatapos ng dealokasyon — ang pag-access sa pinalayang bagay ay nagdudulot ng crash. Ang Unowned ay ginagamit kapag garantisado na ang bagay ay nabubuhay nang hindi bababa sa referensiyang bagay. Karaniwang senaryo — mga closure at relasyon ng magulang-anak na may garantisadong buhay.

swift
class Customer {
    let name: String
    var card: CreditCard?         // strong
    init(name: String) { self.name = name }
    deinit { print("\(name) na-deallocate") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer   // unowned — hindi nagmamay-ari
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card \(number) na-deallocate") }
}

var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer at CreditCard parehong pinakawalan — walang retain cycle

Dito ang CreditCard ay gumagamit ng unowned-referensiya sa Customer. Ang Customer ay nagmamay-ari ng card (strong), at ang card ay hindi nagmamay-ari ng customer (unowned). Kapag ang Customer ay naging nil, parehong bagay ay pinakawalan — walang retain cycle na nalikha. Kung ang card.customer ay strong, ang siklo ay haharang sa pagpapakawala.

Mga karaniwang problema sa ARC at kanilang solusyon

Sa kabila ng automation, ang ARC ay hindi panlunas sa lahat. Ang mga developer ay nakakaranas ng ilang karaniwang problema na nangangailangan ng pag-unawa sa panloob na mekanismo ng pamamahala ng memorya.

Retain Cycle sa mga Closure

Mga Closure sa Swift ay kumukuha ng mga panlabas na variable sa pamamagitan ng strong-referensiya. Kung ang isang closure ay itinalaga sa property ng klase at kumukuha ng self — nagkakaroon ng retain cycle: hawak ng klase ang closure, hawak ng closure ang self. Solusyon — capture list na may weak o 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() { }
}

Ang capture list [weak self] ay lumilikha ng weak-referensiya sa self sa loob ng closure. Ito ay pumutol sa potensyal na retain cycle. Ang guard let self ay ginagarantiyang buhay ang bagay bago isagawa ang code. weak self — karaniwang praktika para sa asynchronus na closure sa Swift.

Pagganap ng retain/release

Kahit na ang retain/release ay magaan na operasyon, ang madalas na pagtaas/pagbaba ng bilang sa mainit na loop ay lumilikha ng overhead. Sa Swift 5.9+, gumagamit ang kompiler ng optimisasyon, kung saan ang sobrang retain/release ay tinatanggal kung mapapatunayan ng analyzer ang kaligtasan. Gayunpaman, sa Objective-C ang retain/release ay maaari pa ring maging bottleneck sa mga high-load na senaryo na may milyun-milyong tawag bawat segundo.

Autorelease Pool

Autorelease Pool — mekanismo ng naantalang release, ginagamit sa Objective-C at ilang senaryo ng Swift. Ang mga bagay ay inilalagay sa pool at nakakatanggap ng release sa pag-drain ng pool. Sa mga loop na may maraming pansamantalang bagay (halimbawa, pag-parse ng JSON), ang paggawa ng sariling autoreleasepool ay nagbabawas ng peak consumption ng operational memory.

Mga Madalas Itanong

Paano naiiba ang ARC sa manu-manong pamamahala ng memorya (MRR)?

Sa manu-manong pamamahala (MRR), ang developer ay tahasang tumatawag ng retain, release, at autorelease. Ang ARC ay awtomatikong naglalagay ng mga tawag na ito sa yugto ng kompilasyon, na nag-aalis ng panganib ng double-free, leak dahil sa nakalimutang release, at error sa pag-balance ng retain/release.

Maaari bang gumana ang ARC sa code na C/C++?

Ang ARC ay namamahala lamang ng mga Objective-C na bagay at Swift class. Para sa mga istruktura at pointer ng C/C++, hindi inilalapat ang ARC — ang mga bagay na ito ay pinamamahalaan nang manu-mano o sa pamamagitan ng C++ smart pointer (shared_ptr, unique_ptr). Ang mga bagay ng Core Foundation (CFString, CGColor) ay hindi rin sakop ng ARC.

Kailan gagamitin ang weak at kailan ang unowned?

weak — kapag ang bagay ay maaaring ma-deallocate bago ang referensiyang bagay (mga delegate, asynchronus na closure). unowned — kapag garantisadong ang bagay ay nabubuhay nang hindi bababa sa referensiyang bagay (magulang-anak, kung saan ang anak ay hindi maaaring umiral nang walang magulang). Kung hindi sigurado — piliin ang weak.

Ano ang mga existential type at paano ito nakakaapekto sa ARC?

Mga existential type (protocol as type) sa Swift ay nagba-package ng halaga sa isang espesyal na lalagyan (existential container). Pinapataas nito ang bilang ng retain/release sa mga hangganan ng protocol. Sa Swift 5.7+, ang opaque result types at some parameter ay nagbabawas ng overhead sa pamamagitan ng pag-alis ng lalagyan.

Paano suriin ang retain count sa Swift?

Walang direktang API para basahin ang retain count sa Swift — ito ay itinuturing na detalye ng implementasyon. Para sa diagnosis, gamitin ang Instruments (Allocations, Leaks) o Memory Debugger sa Xcode. Ang mga tool na ito ay nagpapakita ng bilang ng buhay na instance ng klase at mga retention chain.

Buod

  • ARC — sistema ng pamamahala ng memorya ng kompiler para sa Swift at Objective-C na gumagana sa pamamagitan ng pagbilang ng referensiya
  • Prinsipyo — bawat bagay ay may retain count; kapag sero, ang bagay ay agad at deterministikong pinakawalan
  • Pagkakaiba sa GC — ARC ay gumagana nang walang background thread at Stop-The-World pause, ngunit nangangailangan ng kontrol ng retain cycle
  • Strong — nagtataas ng bilang; weak at unowned — hindi nagtataas, ngunit ang unowned ay hindi nire-reset sa dealokasyon
  • Mga Closure — pangunahing dahilan ng retain cycle sa Swift; capture list [weak self] — karaniwang solusyon
  • Autorelease Pool — mekanismo ng naantalang pagpapakawala para sa pansamantalang bagay sa mga loop at custom na senaryo
  • Diagnosis — Xcode Memory Debugger, Instruments, at LeakCanary (sa pamamagitan ng ObjC bridge) para sa paghahanap ng problema

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din