ریٹین سائیکل ARC میں ایک ایسی صورت حال ہے جب دو یا زائد آبجیکٹساں مضبوط حوالوں کے ذریعے ایک دوسرے کا حوالہ لیتی ہیں، ایک بند لوپ بناتی ہیں۔ Apple Memory Management Guide, 2026 کے مطابق، ریٹین سائیکل چکر میں تمام آبجیکٹس کی آزادی کو روکتا ہے کیونکہ ہر ایک کا retain count ≥ 1 ہوتا ہے۔ GC میں میمری لیک کے برعکس، ریٹین سائیکل اس بات کی ضمانت دیتا ہے کے آبجیکٹس زندہ رہیں جب تک چکر کا کم سے کم ایک باہری رکن زندہ ہو — اور یہاں تک کے تمام باہری حوالوں کے خو جانے کے بعد بھی، اگر چکر علاحدہ ہو۔
اہم نکات
Retain Cycle ایک ایسی صورت حال ہے جس میں دو یا زائد آبجیکٹساں مضبوط حوالوں کے ذریعے ایک دوسرے کی مالک ہوتی ہیں، ایک بند انحصار گراف بناتی ہیں۔ ARC ان میں سے کسی بھی آبجیکٹ کو آزاد نہیں کر سکتا کیونکہ ہر ایک کا retain count ہمیشہ ≥ 1 ہوتا ہے: آبجیکٹ A B کو رکھتا ہے، B A کو رکھتا ہے، اور ان کے کاؤنٹر کبھی صفر نہیں ہوتے۔
مسئلہ صرف حوالے گننے کے نظاموں (ARC، MRR) میں پیدا ہوتا ہے۔ کوڑا کہکرہ (Garbage Collection) میں، کہکرنے والا جر سیٹ سے حوالے گراف کے ذریعے ناقابل رسائی کا تعین کرتا ہے — چکر ریکاوٹ نہیں ہیں۔ ARC میں، تاہم، ایک چکر ایک لیک کے برابر ہے، کیونکہ حتمی آزادی گننے کمنظامی کے ذریعے چکری انحصار کو حل نہیں کر سکتا۔
WWDC 2012 Session 406 کے مطابق، retain cycle Objective-C اور Swift ایپلیکیشنز میں میمری لیک کی سب سے عام وجہ ہے۔ مامولی مناظر: ڈیلیگٹس کے ساتھ والد-والدہ تعلقات، self کو کیپچر کرنے والے کلوزرز، اور دو طرفہ تعلقات کے ساتھ پرتیدار ساختاریں۔
آئیا کلاسکی retain cycle مناظر کا جائزہ لیتے ہیں جن کا سامنا ہر iOS ڈیولپر کرتا ہے۔ ان پیٹرنز کو سمجھنا ARC کے ساتھ محفوظ کوڈ لکھنے کی بنیاد ہے۔
کلاسکی منظر: ایک والد آبجیکٹ (مثال کے طور پر، UIViewController) ایک تحلیلی آبجیکٹ بناتا ہے اور اس کا ڈیلیگٹ بن جاتا ہے۔ اگر دونوں مضبوط حوالے استعمال کرتے ہیں، تو ایک retain cycle پیدا ہوتا ہے۔ حل — ڈیلیگٹ کو weak ہونا چاہی۔
// غلطی: strong delegate کے ذریعے retain cycle
protocol ChildDelegate: AnyObject { }
class ParentVC: UIViewController, ChildDelegate {
var child: ChildVC?
func showChild() {
child = ChildVC()
child?.delegate = self // Parent → Child (strong)
} // Child → Parent (delegate کے ذریعے strong)
} // ⚠️ Retain cycle!
class ChildVC: UIViewController {
var delegate: ChildDelegate? // ❌ دیفالٹ strong
}
// تصحیح: weak delegate
class ChildVC: UIViewController {
weak var delegate: ChildDelegate? // ✅ weak — نہیں رکھتا
}
مثال میں، ParentVC child خاصیت کے ذریعے ChildVC پر ایک مضبوط حوالہ رکھتا ہے۔ ChildVC delegate کے ذریعے ParentVC پر ایک مضبوط حوالہ رکھتا ہے۔ چکر بند ہے۔ تصحیح: weak var delegate — حوالہ retain count نہیں بڑھاتا، اور ParentVC آزاد ہو سکتا ہے۔
NSTimer retain cycle کا ایک کلاسکی مڠی ہے۔ ٹائمر اپنے ہدف (عموما self) کو رکھتا ہے، اور ہدف ایک خاصیت کے ذریعے ٹائمر کو رکھتا ہے۔ چاہے ٹائمر ایک بار کا ہو، یہ invalidate کرنے تک آزاد نہیں ہوگا۔ حل: deinit یا viewDidDisappear میں ہمیشہ timer.invalidate() کو کال کریں۔
ساکڈیا مالکیت والے ساختاروں (کوئرڈینیٹرز، روٹرز) میں، اکثر ملٹی-سٹپ چکر پیدا ہوتے ہیں: Coordinator → ViewController → ViewModel → Coordinator (کالبیک کے ذریعے)۔ زنجیر میں ہر مضبوط حوالے کو شعوری طور پر چنا جانا چاہی۔ — کسی بھی کڒی میں ایک weak حوالہ چکر کو توڒ دیتا ہے۔
Swift میں کلوزرز مضبوط حوالے سے باہری متغیرات کو کیپچر کرتے ہیں۔ اگر ایک کلوزر کسی آبجیکٹ کی خاصیت (مثال کے طور پر، completion handler) کے طور پر محفوظ کیا جاتا ہے اور self کو کیپچر کرتا ہے، تو یہ ایک retain cycle بناتا ہے: self → closure → self۔
یہ جدید Swift ڈیولپمنٹ میں retain cycle کا سب سے عام مذارع ہے۔ یہ تضمنی طور پر ہوتا ہے — ایک ڈیولپر کسی کلوزر میں self کیپچر پر توجہ نہیں دے سکتا، خاص کر کر جب واضح self کے بغیر مختصر سنٹکس استعمال کیا جاتا ہے۔
class DownloadService {
var onComplete: ((Data) -> Void)?
var result: Data?
func startDownload() {
// ❌ Retain cycle: self → onComplete → self
onComplete = { data in
self.result = data
self.notifyUI()
}
// ✅ تصحیح: weak self کے ساتھ کیپچر لسٹ
onComplete = { [weak self] data in
guard let self else { return }
self.result = data
self.notifyUI()
}
}
func notifyUI() { }
}
ایک کیپچر لسٹ [weak self] کلوزر کے اندر self کا ایک کمزور حوالہ بناتا ہے۔ اگر کلوزر کے انجام سے پہلے DownloadService آزاد ہو جاتا ہے، تو self nil ہو جاتا ہے، اور کوڈ guard کے ذریعے محفوظ طور پر باہر نکل جاتا ہے۔ Swift میں غیر سینکرونس کلوزرز کے لیے یہ ایک معیاری پیٹرن ہے — جب بھی کوئی کلوزر ایک خاصیت کے طور پر محفوظ کیا جائے، تو اسے استعمال کرنا چاہی۔
unowned self weak self کا ایک متبادل ہے جب self کے کلوزر سے زیادہ زندہ رہنے کی ضمانت ہو۔ مثال: متزامن کلوزرز جو فورن انجام پاتے ہیں (sorted، filter)۔ ان معاملات میں self یقیناً زندہ ہے، اور unowned محفوظ ہے۔ تاہم، unowned آزاد کیگے گئے آبجیکٹ تک رسائی پر کریش کرتا ہے — اس لئے weak کو پہلے سے محفوظ دیفالٹ انتخاب سمجھا جاتا ہے۔
ایپلیکیشن کی کارکردگی کے لیے retain cycle کو ابتدائی مرحلہ میں پتا لگانا انتہائی اہم ہے۔ آئیا iOS ڈیولپمنٹ میں چکری حوالوں کی پہچان کے لیے بنیادی اوزار اور تکنیکوں کا جائزہ لیت۔ ہیں۔
Xcode Memory Debugger (Debug Memory Graph) ایک مرئی اوزار ہے جو میمری میں آبجیکٹس کا گراف ان کے حوالوں کے ساتھ دیڤخاتا ہے۔ ایک retain cycle مضبوط تیروں کی ایک بند زنجیر کے طور پر ظاہر ہوتا ہے۔ کھولنے کے لیے: ایپ چلانے کے دوران Debug area پینل میں Debug Memory Graph بٹن پر کلیک کریں۔ ہر آبجیکٹ اپنے قسم، پتہ اور حوالوں کی فہرست کے ساتھ دیڤۚخایا جاتا ہے۔
Instruments Leaks خودکار لیک پتا لگانے کا ایک پروفائلر ہے۔ یہ تخصیصات ریکارڈ کرتا ہے اور حقیقی وقت میں حوالے گراف کا تجزیہ کرتا ہے۔ یہ نہ صرف retain cycle بلکی بھولے گئے حوالے، آزاد نہ کیے گئے ViewController، اور دیگر لیکز کا بھی پتا لگاتا ہے۔ Leaks عین آبجیکٹ اور انتظام زنجیر کی ضمانت دیتا ہے۔
سب سے سادہ طریقہ ہر اہم کلاس کے deinit میں print شامل کرنا ہے۔ اگر آبجیکٹ کے تباہ ہونے کی توقع پر deinit نہیں بلایا جاتا، تو ایک retain cycle ہے۔ اس طریقے کے لیے کسی اوزار کی ضرورت نہیں ہے اور یہ ابتدائی تشخیص کے لیے مؤثر ہے۔
| اوزار | قسم | کب استعمال کریں |
|---|---|---|
| Memory Debugger | مرئی گراف | نیویگیشن کے بعد دستی جاؤنچ |
| Instruments Leaks | خودکار تجزیہ | ریگریشن ٹیسٹنگ، CI |
| deinit print | دستی لاگنگ | ڈیولپمنٹ، کوڈ کا جائزہ |
| Malloc Scribble | ران ٹائم فلیگ | use-after-free کی ڈیباگنگ |
تجویز کردہ طریقہ: ڈیولپمنٹ کے دوران deinit لاگنگ، دستی ٹیسٹنگ میں Memory Debugger، اور خودکار ریگریشن لیک پتا لگانے کے لیے CI/CD پائپ لائن میں Instruments Leaks استعمال کریں۔
Retain cycle کو پیدا ہونے سے روکنا انہیں پیداور میں ٹھیک کرنے سے آسان ہے۔ یہاں کچھ ایسے اصول ہیں جو چکری حوالوں کے خطرے کو کم کرتے ہیں۔
تمام ڈیلیگٹس اور dataSource weak ہونا چاہی۔ یہ اصول UIKit میں ضمنی ہے: Apple SDK میں تمام ڈیلیگٹ پروٹوکلز weak خاصیتوں کے ساتھ مقرر کیے جاتے ہیں (UITableView.delegate، UICollectionView.dataSource)۔ اپنے پروٹوکلز کے لیے، weak var delegate: MyDelegate? استعمال کریں اور پروٹوکل کو AnyObject سے وراثت دیں۔
کوئی بھی کلوزر جو ایک خاصیت (completion handler، callback) کے طور پر محفوظ ہو اور self کو کیپچر کرتا ہو، اپنی کیپچر لسٹ میں [weak self] استعمال کرنا چاہی۔ استثناء وہ کلوزرز ہیں جو فورن انجام پاتے ہیں اور محفوظ نہیں ہوتے (sorted، map، filter)۔ ان کے لیے، unowned self محفوظ ہے۔
پیچیدہ ساختاروں (VIPER، Coordinators، Redux) میں، مضبوط حوالوں کی سمت کا پتا لگائیں۔ مالک ماتحت پر مضبوط حوالہ رکھتا ہے، لیکن ماتحت کو مالک کا حوالہ صرف weak یا unowned کے ذریعے لینا چاہی۔ ایک جهتہ ڈیٹا بھاو حوالوں کے انتظام کو آسان بناتا ہے۔
// مثال: deinit لاگنگ کے ساتھ جائزہ
class BaseViewController: UIViewController {
deinit {
print("✅ \(type(of: self)) deallocated")
}
}
// استعمال: تمام ViewController BaseViewController سے وراثت پاتے ہیں
class ProfileVC: BaseViewController {
var viewModel: ProfileViewModel?
var onLogout: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onLogout = { [weak self] in
self?.dismiss(animated: true)
}
}
}
// ProfileVC بند کرتے وقت کانسول میں "✅ ProfileVC deallocated" کی توقع
deinit لاگنگ کے ساتھ ایک بیس کلاس فوری رائے فراہم کرتا ہے۔ اگر سکرین بند ہونے پر پیغام ظاہر نہیں ہوتا، تو اس کلاس میں retain cycle ہے۔ تمام ViewControllerز کے لیے اس عمل کو پروجیکٹ ٹیمپلیٹ میں شامل کریں۔
اکثر پوچے جانے والے سوالات
Retain cycle ایک مخصوص ARC مسئلہ ہے جہاں مضبوط حوالوں کا ایک بند لوپ آزادی کو روکتا ہے۔ GC میں، کہکرنے والا حوالی کاؤنٹروں کو نہیں بلکہ جر سیٹ سے رسائی کا تجزیہ کرتا ہے — اس لئے چکر لیک نہیں ہیں۔ ARC میں، تاہم، کائی علاحدہ چکر ایک یقینی لیک ہے۔
ایک weak حوالہ کسی آبجیکٹ کے retain count میں اضافہ نہیں کرتا۔ اگر آپ ایک چکر میں ایک مضبوط حوالے کو weak سے بدلتے ہیں، تو ہر آبجیکٹ کا retain count صفر تک پہگںچ سکتا ہے۔ آبجیکٹ کے آزاد ہونے کے بعد، weak حوالہ خودکار طور پر nil پر مقرر ہو جاتا ہے، جو آزاد شدہ میمری تک رسائی کو روکتا ہے۔
ہاں، ایک retain cycle کسی بھی تعداد میں آبجیکٹس شامل کر سکتا ہے: A → B → C → A۔ اسے توڒنے کے لیے، آپکو صرف ایک کڒی کو توڒنے کی ضرورت ہے — کسی بھی مضبوط حوالے کو weak یا unowned سے بدلیں۔ اوزار صرف آبجیکٹس کے جوڒے نہیں بلکہ پورا گراف دیڤۚخاتے ہیں۔
GCD (Grand Central Dispatch) انجام کے بعد کلوزر کو محفوظ نہیں کرتا۔ DispatchWorkItem انجام پاتا ہے اور آزاد ہو جاتا ہے، چاہے کلوزر self کو کیپچر کرے یا نہیں۔ Retain cycle صرف اس وقت ہوتا ہے جب ایک کلوزر ایک خاصیت (ایک کلاس میں completion handler) کے طور پر محفوظ کیا جاتا ہے، نہ کہ جب اسے کیو میں دیا جاتا ہے۔
Instruments Leaks ہمیشہ عارضی retain cycle (سیکنڈ تک رہنے والے) یا بریجینگ کے ذریعے C/C++ آبجیکٹس میں چکری حوالے پتا نہیں لگا پاتا۔ مکمل جائزہ کے لیے، سین میں تمام اہم آبجیکٹس کے deinit لاگنگ کے ساتھ Memory Debugger دستی طور پر استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں