Automatic Reference Counting (ARC) Swift اور Objective-C میں ایک میموری مینجمنٹ سسٹم ہے جو خود بخود ہر آبجیکٹ کے حوالوں کی تعداد شمار کرتا ہے اور کاؤنٹر کے صفر ہونے پر اسے آزاد کر دیتا ہے۔ Apple Swift دستاویز، 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 میں منتقلی نے Apple ایپلیکیشنز میں میموری سے متعلق کریش بگز کو 70% تک کم کر دیا۔ ڈویلپرز نے دستی طور پر retain/release متوازن کرنا چھوڑ دیا، جس سے لیک اور double-free غلطیوں کی پوری کلاس ختم ہو گئی۔
میموری میں ہر آبجیکٹ کا ایک ریفرنس کاؤنٹر (retain count) ہوتا ہے۔ جب کوئی آبجیکٹ بنایا جاتا ہے، کاؤنٹر 1 پر سیٹ ہوتا ہے۔ جب ایک نیا strong حوالہ آبجیکٹ کی طرف اشارہ کرتا ہے — کاؤنٹر بڑھتا ہے (retain)۔ جب strong حوالہ غائب ہو جاتا ہے — کاؤنٹر گھٹتا ہے (release)۔ صفر تک پہنچنے پر، آبجیکٹ فوری طور پر آزاد ہو جاتا ہے۔
Swift کمپائلر ہر تفویض پر retain/release داخل نہیں کرتا — یہ اصلاح کے لیے جامد تجزیہ استعمال کرتا ہے۔ مثال کے طور پر، اگر کوئی آبجیکٹ منتقل کرنے کے بعد استعمال نہ ہونے کی ضمانت ہو، کمپائلر غیر ضروری release/retain چھوڑ سکتا ہے۔ اس اصلاح کو ARC Optimization کہا جاتا ہے۔
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 دائرہ کار سے باہر جاتا ہے
// 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 ms) |
| اضافی بوجھ | ہر حوالے پر کاؤنٹر میں اضافہ/کمی | آبجیکٹ گراف کا چکر، نشان زد کرنا، صاف کرنا |
| مسائل | Retain Cycle (دستی حل) | ہیپ کا بکھراؤ، بھولے ہوئے حوالوں سے لیک |
| اضافی تھریڈ | ضرورت نہیں | کوڑا کرکٹ جمع کرنے والے تھریڈ کی ضرورت |
اہم سودا: ARC پیش قیاسی آبجیکٹ زندگی اور صفر وقفہ فراہم کرتا ہے، لیکن ڈویلپر سے retain cycles کو سمجھنے اور 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 cycles توڑنے کے لیے استعمال ہوتے ہیں، خاص طور پر delegate پیٹرن میں۔
Unowned ایک غیر مالک حوالہ ہے، جو weak کی طرح retain count نہیں بڑھاتا۔ تاہم، unowned حوالہ آزادی کے بعد nil پر سیٹ نہیں ہوتا — آزاد شدہ آبجیکٹ تک رسائی کریش کا سبب بنتی ہے۔ Unowned اس وقت استعمال ہوتا ہے جب ضمانت ہو کہ آبجیکٹ کم از کم حوالہ لینے والے آبجیکٹ جتنا زندہ رہتا ہے۔ عام استعمال کے کیسز closures اور ضمانتی زندگی کے ساتھ والدین-بچے کے تعلقات ہیں۔
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 — مالک نہیں
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 اور CreditCard دونوں آزاد — کوئی retain cycle نہیں
یہاں CreditCard Customer کے لیے unowned حوالہ استعمال کرتا ہے۔ Customer کارڈ کا مالک ہے (strong)، اور کارڈ گاہک کا مالک نہیں ہے (unowned)۔ جب Customer آزاد ہوتا ہے، دونوں آبجیکٹ آزاد ہو جاتے ہیں — کوئی retain cycle نہیں ہوتا۔ اگر card.customer strong ہوتا، تو چکر آزادی کو روک دیتا۔
آٹومیشن کے باوجود، ARC کوئی علاج نہیں ہے۔ ڈویلپرز کو کئی عام مسائل کا سامنا کرنا پڑتا ہے جن کے لیے اندرونی میموری مینجمنٹ میکانزم کی سمجھ کی ضرورت ہوتی ہے۔
Swift میں closures بیرونی متغیرات کو strong حوالہ سے پکڑتے ہیں۔ اگر ایک closure کسی کلاس خصوصیت کو تفویض کیا جائے اور self کو پکڑے — ایک retain cycle پیدا ہوتا ہے: کلاس closure کو رکھتی ہے، closure self کو رکھتا ہے۔ حل 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() { }
}
کیپچر لسٹ [weak self] closure کے اندر self کے لیے weak حوالہ بناتی ہے۔ یہ ممکنہ retain cycle کو توڑتا ہے۔ Guard let self کوڈ چلانے سے پہلے آبجیکٹ کے زندہ ہونے کی ضمانت دیتا ہے۔ weak self Swift میں غیر متزامن closures کے لیے معیاری عمل ہے۔
اگرچہ retain/release ہلکی کارروائیاں ہیں، گرم لوپ میں بار بار کاؤنٹر میں اضافہ/کمی اضافی بوجھ ڈالتا ہے۔ Swift 5.9+ میں، کمپائلر اصلاح استعمال کرتا ہے جو بے کار retain/release کو ہٹا دیتا ہے اگر تجزیہ کار اسے محفوظ ثابت کرے۔ تاہم، Objective-C میں، retain/release فی سیکنڈ لاکھوں کالوں والے زیادہ بوجھ کے منظرناموں میں رکاوٹ بن سکتے ہیں۔
Autorelease Pool ایک التوا والی رہائی کا میکانزم ہے جو Objective-C اور کچھ Swift منظرناموں میں استعمال ہوتا ہے۔ آبجیکٹ کو پول میں رکھا جاتا ہے اور پول کے خالی ہونے پر release ملتا ہے۔ بہت سے عارضی آبجیکٹ (مثلاً JSON پارسنگ) والے لوپ میں، حسب ضرورت autoreleasepool بنانے سے چوٹی میموری کی کھپت کم ہوتی ہے۔
اکثر پوچھے گئے سوالات
دستی مینجمنٹ (MRR) میں، ڈویلپر واضح طور پر retain، release اور autorelease کال کرتا تھا۔ ARC ان کالوں کو کمپائلیشن مرحلے پر خود بخود داخل کرتا ہے، double-free کے خطرے، بھولے ہوئے release سے لیک اور retain/release توازن کی غلطیوں کو ختم کرتا ہے۔
ARC صرف Objective-C آبجیکٹ اور Swift کلاسز کا انتظام کرتا ہے۔ C/C++ ڈھانچوں اور پوائنٹرز کے لیے ARC لاگو نہیں ہوتا — یہ آبجیکٹ دستی طور پر یا C++ سمارٹ پوائنٹرز (shared_ptr, unique_ptr) کے ذریعے منظم کیے جاتے ہیں۔ Core Foundation آبجیکٹ (CFString, CGColor) بھی ARC کے دائرہ کار میں نہیں آتے۔
weak — جب آبجیکٹ حوالہ لینے والے آبجیکٹ سے پہلے آزاد ہو سکتا ہے (نمائندگان، غیر متزامن closures)۔ unowned — جب ضمانت ہو کہ آبجیکٹ کم از کم حوالہ لینے والے جتنا زندہ رہتا ہے (والدین-بچے جہاں بچہ والدین کے بغیر موجود نہیں رہ سکتا)۔ یقین نہ ہو تو weak منتخب کریں۔
Swift میں وجودی اقسام (protocol بطور قسم) قدر کو ایک خاص کنٹینر (existential container) میں لپیٹتی ہیں۔ یہ پروٹوکول حدود پر retain/release کی تعداد بڑھاتا ہے۔ Swift 5.7+ میں، غیر شفاف نتیجہ کی اقسام (opaque result types) اور some پیرامیٹرز کنٹینر کو ہٹا کر اضافی بوجھ کم کرتے ہیں۔
Swift میں retain count پڑھنے کے لیے کوئی براہ راست API نہیں ہے — اسے نفاذ کی تفصیل سمجھا جاتا ہے۔ تشخیص کے لیے، Xcode میں Instruments (Allocations, Leaks) یا میموری ڈیبگر استعمال کریں۔ یہ اوزار زندہ کلاس مثالوں کی تعداد اور برقرار رکھنے کی زنجیریں دکھاتے ہیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں