Automatic Reference Counting (ARC), Swift ve Objective-C'de her nesneye yapılan referans sayısını otomatik olarak sayan ve sayaç sıfıra ulaştığında nesneyi serbest bırakan bir bellek yönetim sistemidir. Apple Swift Belgeleri, 2026'ya göre ARC, derleyiciye gömülüdür ve derleme zamanında çalışarak uygun yerlere retain/release çağrıları ekler. Garbage Collection'ın aksine ARC, ayrı bir toplayıcı iş parçacığı gerektirmez ve uygulama çalışması sırasında duraklamalar oluşturmaz.
Önemli Noktalar
ARC (Automatic Reference Counting), Apple tarafından Xcode 4.2'de (2011) Objective-C için tanıtılan ve Swift tarafından miras alınan derleyici tabanlı bir bellek yönetim mekanizmasıdır. Manuel bellek yönetiminden (Manual Retain-Release, MRR) farklı olarak ARC, retain, release ve autorelease çağrılarını tamamen otomatikleştirir ve geliştirici müdahalesi olmadan derleme zamanında ekler.
ARC bir çöp toplayıcı değildir. Dinamik kod ekleme ile statik analizdir: derleyici nesne ömürlerini analiz eder ve nesnelerin oluşturulduğu, kopyalandığı veya kapsam dışına çıktığı noktalara retain/release yerleştirir. Sonuç deterministik bellek serbest bırakmadır: nesne, ona işaret eden hiçbir referans kalmadığında, gecikme veya duraklama olmadan tam olarak o anda silinir.
WWDC 2011 Session 323'e göre, MRR'den ARC'ye geçiş, Apple uygulamalarındaki bellek ile ilgili çökme hatalarını %70 oranında azaltmıştır. Geliştiriciler manuel olarak retain/release dengesi kurmayı bırakmış, bu da bir bütün sızıntı ve double-free hatası sınıfını ortadan kaldırmıştır.
Bellekteki her nesnenin bir referans sayacı (retain count) vardır. Bir nesne oluşturulduğunda sayaç 1 olarak ayarlanır. Yeni bir strong referans nesneyi işaret ettiğinde sayaç artar (retain). Bir strong referans kaybolduğunda sayaç azalır (release). Sıfıra ulaştığında nesne hemen serbest bırakılır.
Swift derleyicisi her atamada retain/release eklemez — optimizasyon için statik analiz kullanır. Örneğin, bir nesnenin aktarıldıktan sonra kullanılmayacağı garanti edilirse, derleyici gereksiz bir release/retain'i atlayabilir. Bu optimizasyona ARC Optimization denir.
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) initialized (retain count: 1)")
}
deinit {
print("\(name) deallocated")
}
}
func testARC() {
let p = Person(name: "Alice") // retain count = 1
let q = p // retain count = 2
// q kapsam dışına çıkar
// retain count = 1
// p kapsam dışına çıkar
// retain count = 0 → deinit
}
Bu örnek, ARC'nin sayacı nasıl yönettiğini gösterir: q = p atandığında sayaç artar; q kapsam dışına çıktığında azalır. Son strong referans kaybolduğunda, yıkıcı hemen çağrılır. Hiçbir çöp toplayıcı beklemez — bellek anında serbest bırakılır.
ARC ve Garbage Collection aynı sorunu — otomatik bellek yönetimini — ancak temelde farklı yaklaşımlarla çözer. Aralarındaki seçim dil mimarisini belirler: Swift (ARC) vs Java/Go (GC). Temel farklara bakalım.
| Özellik | ARC (Swift/ObjC) | GC (Java/Go) |
|---|---|---|
| Serbest bırakma zamanı | Deterministik: sayaç sıfırlandığında hemen | Deterministik olmayan: sonraki toplama döngüsünde |
| Yürütme duraklamaları | Yok (retain/release derleme zamanında eklenir) | Stop-The-World duraklamaları (2–200 ms) |
| Ek yük | Her referansta sayaç artırma/azaltma | Nesne grafiği gezinme, işaretleme, temizleme |
| Sorunlar | Retain Cycle (manuel çözüm) | Yığın parçalanması, unutulan referanslardan sızıntılar |
| Ek iş parçacığı | Gerekmez | Çöp toplayıcı iş parçacığı gerekir |
Temel ödünleşim: ARC öngörülebilir nesne ömürleri ve sıfır duraklama sağlar, ancak geliştiricinin retain cycle'ları anlamasını ve weak/unowned'ı doğru seçmesini gerektirir. GC, geliştiriciyi bu endişelerden kurtarır, ancak deterministik olmayan duraklamalar ve ek bir iş parçacığı pahasına.
ARC, her biri sayacı ve nesne yaşam döngüsünü farklı şekilde etkileyen üç tür referans niteleyicisi tanımlar. Doğru niteleyiciyi seçmek, Swift'te güvenli bellek yönetiminin temelidir.
Strong varsayılan niteleyicidir. Her strong referans, nesnenin retain count'unu 1 artırır. En az bir strong referans var olduğu sürece nesne canlı kalır. Swift'teki tüm sınıf özellikleri ve yerel değişkenler varsayılan olarak strong'dur. Strong referanslar bir sahiplik ilişkisi oluşturur: A nesnesi B nesnesine sahiptir.
Weak, retain count'u artırmayan bir referanstır. Bir weak referans nesneyi işaret etse bile nesne serbest bırakılabilir. Serbest bırakıldıktan sonra weak referans otomatik olarak nil olarak ayarlanır. Weak referanslar her zaman var olarak isteğe bağlı türle (?) bildirilir. Özellikle delegate deseninde retain cycle'ları kırmak için kullanılır.
Unowned, weak gibi retain count'u artırmayan sahip olmayan bir referanstır. Ancak, unowned referans serbest bırakıldıktan sonra nil olarak ayarlanmaz — serbest bırakılmış bir nesneye erişmek çökmeye neden olur. Unowned, nesnenin en az referans alan nesne kadar yaşayacağı garanti edildiğinde kullanılır. Tipik kullanım durumları closure'lar ve garantili yaşam süresine sahip ebeveyn-çocuk ilişkileridir.
class Customer {
let name: String
var card: CreditCard? // strong
init(name: String) { self.name = name }
deinit { print("\(name) deallocated") }
}
class CreditCard {
let number: String
unowned let customer: Customer // unowned — sahip değil
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card \(number) deallocated") }
}
var customer: Customer? = Customer(name: "Bob")
customer?.card = CreditCard(number: "1234", customer: customer!)
customer = nil
// Customer ve CreditCard ikisi de serbest bırakıldı — retain cycle yok
Burada CreditCard, Customer'a unowned referans kullanır. Customer karta sahiptir (strong), ve kart müşteriye sahip değildir (unowned). Customer serbest bırakıldığında, her iki nesne de serbest bırakılır — retain cycle oluşmaz. card.customer strong olsaydı, döngü serbest bırakmayı engellerdi.
Otomasyona rağmen ARC her derde deva değildir. Geliştiriciler, iç bellek yönetim mekanizmasını anlamayı gerektiren birkaç tipik sorunla karşılaşır.
Swift'teki closure'lar dış değişkenleri strong referansla yakalar. Bir closure bir sınıf özelliğine atanır ve self'i yakalarsa, bir retain cycle oluşur: sınıf closure'ı tutar, closure self'i tutar. Çözüm, weak veya unowned ile bir yakalama listesidir.
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() { }
}
Yakalama listesi [weak self], closure içinde self'e weak bir referans oluşturur. Bu, potansiyel retain cycle'ı kırar. Guard let self, kodu çalıştırmadan önce nesnenin canlı olduğunu garanti eder. weak self, Swift'teki asenkron closure'lar için standart uygulamadır.
Retain/release hafif işlemler olsa da, sıcak döngülerde sık sayaç artırma/azaltma ek yük ekler. Swift 5.9+'da derleyici, analizcinin güvenli olduğunu kanıtlaması durumunda gereksiz retain/release'leri kaldıran bir optimizasyon kullanır. Ancak Objective-C'de, retain/release saniyede milyonlarca çağrı içeren yüksek yük senaryolarında hala darboğaz olabilir.
Autorelease Pool, Objective-C ve bazı Swift senaryolarında kullanılan ertelenmiş bir serbest bırakma mekanizmasıdır. Nesneler havuza yerleştirilir ve havuz boşaltıldığında release alır. Çok sayıda geçici nesne içeren döngülerde (örneğin JSON ayrıştırma), özel bir autoreleasepool oluşturmak, pik bellek tüketimini azaltır.
Sıkça Sorulan Sorular
Manuel yönetimde (MRR), geliştirici açıkça retain, release ve autorelease çağırırdı. ARC bu çağrıları derleme zamanında otomatik olarak ekler, double-free riskini, unutulan release'den kaynaklanan sızıntıları ve retain/release dengesi hatalarını ortadan kaldırır.
ARC yalnızca Objective-C nesnelerini ve Swift sınıflarını yönetir. C/C++ yapıları ve işaretçileri için ARC uygulanmaz — bu nesneler manuel olarak veya C++ akıllı işaretçileri (shared_ptr, unique_ptr) aracılığıyla yönetilir. Core Foundation nesneleri (CFString, CGColor) da ARC kapsamında değildir.
weak — nesne, referans alan nesneden önce serbest bırakılabildiğinde (temsilciler, asenkron closure'lar). unowned — nesnenin en az referans alan nesne kadar yaşayacağı garanti edildiğinde (çocuğun ebeveyn olmadan var olamayacağı ebeveyn-çocuk). Emin değilseniz weak seçin.
Swift'teki varoluşsal türler (protokol tür olarak), değeri özel bir kapsayıcıya (existential container) sarar. Bu, protokol sınırlarında retain/release sayısını artırır. Swift 5.7+'da, opak sonuç türleri ve some parametreleri, kapsayıcıyı ortadan kaldırarak ek yükü azaltır.
Swift'te retain count'u okumak için doğrudan bir API yoktur — bu bir uygulama detayı olarak kabul edilir. Teşhis için Xcode'da Instruments (Allocations, Leaks) veya Bellek Hata Ayıklayıcısı'nı kullanın. Bu araçlar, canlı sınıf örneklerinin sayısını ve tutma zincirlerini gösterir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun