रिटेन साइकल — सार, उत्पत्ति के कारण और ऐप डेवलपमेंट में निवारण

लेखक: IT Sectr प्रकाशित: 2026-03-29 पढ़ने का समय: 8 मिनट

रिटेन साइकल (Retain Cycle) ARC में एक स्थिति है जहां दो या अधिक ऑब्जेक्ट strong संदर्भों के माध्यम से एक-दूसरे का संदर्भ लेते हैं, एक बंद लूप बनाते हुए। Apple Memory Management Guide, 2026 के अनुसार, retain cycle चक्र में सभी ऑब्जेक्टों की रिलीज को अवरुद्ध करता है क्योंकि प्रत्येक का retain count ≥ 1 होता है। GC में मेमरी लीक के विपरीत, retain cycle गारंटी देता है कि ऑब्जेक्ट तब तक जीवित रहेंगे जब तक चक्र का कम से कम एक बाहरी सदस्य जीवित है — और यहाँ तक कि सभी बाहरी संदर्भों के खो जाने के बाद भी, यदि चक्र पृथक है।

मुख्य बातें

  • रिटेन साइकल — strong संदर्भों की एक बंद श्रृंखला जिसमें ऑब्जेक्ट ARC द्वारा मुक्त नहीं किए जा सकते
  • कारण — दो (या अधिक) ऑब्जेक्ट एक-दूसरे पर strong संदर्भ रखते हैं, retain count का शून्य होना असंभव है
  • परिणाम — मेमरी लीक: ऑब्जेक्ट हमेशा के लिए मेमरी में रहते हैं, RAM की खपत बढ़ती है
  • समाधान — चक्र में एक strong संदर्भ को weak या unowned से बदलना
  • निदान — Xcode Memory Debugger, Instruments Leaks, Debug Memory Graph

Retain Cycle क्या है?

Retain Cycle एक ऐसी स्थिति है जिसमें दो या अधिक ऑब्जेक्ट strong संदर्भों के माध्यम से एक-दूसरे के मालिक होते हैं, एक बंद निर्भरता ग्राफ बनाते हुए। ARC इनमें से किसी भी ऑब्जेक्ट को मुक्त नहीं कर सकता क्योंकि प्रत्येक का retain count हमेशा ≥ 1 होता है: ऑब्जेक्ट A, B को रखता है, B, A को रखता है, और उनके काउंटर कभी शून्य नहीं होते।

यह समस्या विशेष रूप से संदर्भ गणना प्रणालियों (ARC, MRR) में होती है। कचरा संग्रहण (Garbage Collection) में, संग्रहक रूट सेट से संदर्भ ग्राफ के माध्यम से अपथ्यता निर्धारित करता है — चक्र कोई बाधा नहीं हैं। ARC में, हालाँकि, एक चक्र लीक के समान है, क्योंकि निर्धारित मुक्ति गणना के माध्यम से चाक्रिक निर्भरताओं को हल नहीं कर सकती।

WWDC 2012 Session 406 के अनुसार, retain cycle Objective-C और Swift एप्लिकेशनों में मेमरी लीक का सबसे सामान्य कारण है। विशिष्ट परिदृश्य: डेलीगेट के साथ मात्र-पित्र संबंध, self को कैप्चर करने वाले क्लोजर, और द्वि-दिशात्मक संबंधों वाली स्तरित आर्किटेक्चर्स।

iOS डेवलपमेंट में retain cycle के उदाहरण

आइए क्लासिक retain cycle परिदृश्यों की जाँच करें जिनका सामना प्रत्येक iOS डेवलपर करता है। इन पैटर्नों को समझना ARC के साथ सुरक्षित कोड लिखने का आधार है।

डेलीगेट के साथ Parent-Child

क्लासिक परिदृश्य: एक मातृ ऑब्जेक्ट (उदाहरण के लिए, UIViewController) एक चाइल्ड ऑब्जेक्ट बनाता है और उसका डेलीगेट बन जाता है। यदि दोनों strong संदर्भों का उपयोग करते हैं, तो retain cycle होता है। समाधान — डेलीगेट weak होना चाहिए।

swift
// त्रुटि: 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 पर strong संदर्भ रखता है। ChildVC delegate के माध्यम से ParentVC पर strong संदर्भ रखता है। चक्र बंद है। सुधार: weak var delegate — संदर्भ retain count नहीं बढ़ाता, और ParentVC मुक्त हो सकता है।

NSTimer और retain cycle

NSTimer retain cycle का एक क्लासिक स्रोत है। टाइमर अपने लक्ष्य (आमतौर पर self) को रोके रखता है, और लक्ष्य एक संपत्ति के माध्यम से टाइमर को रोके रखता है। भले ही टाइमर एक-बार का हो, यह invalidate काल किए जाने तक मुक्त नहीं होगा। समाधान: deinit या viewDidDisappear में हमेशा timer.invalidate() को कॉल करें।

स्तरित आर्किटेक्चर्स

कैस्केडिंग स्वामित्व वाले आर्किटेक्चर्स (कोआर्डिनेटर, रूटर) में, अक्सर मल्टी-स्टेप चक्र होते हैं: Coordinator → ViewController → ViewModel → Coordinator (कॉलबैक के माध्यम से)। श्रृंखला में प्रत्येक strong संदर्भ को सावधानीपूर्वक चुना जाना चाहिए — किसी भी कड़ी में एक weak संदर्भ चक्र को तोड़ देता है।

Swift क्लोजर में Retain Cycle

Swift में क्लोजर बाहरी चर को strong संदर्भ से कैप्चर करते हैं। यदि किसी ऑब्जेक्ट की संपत्ति के रूप में एक क्लोजर (उदाहरण के लिए, completion handler) संग्रहीत किया जाता है और self को कैप्चर करता है, तो यह एक retain cycle बनाता है: self → closure → self

आधुनिक Swift डेवलपमेंट में यह retain cycle का सबसे आम स्रोत है। यह अप्रत्यक्ष रूप से होता है — एक डेवलपर क्लोजर में self के कैप्चर पर ध्यान नहीं दे सकता, खासकर जब आप स्पष्ट self के बिना शॉर्टहैंड सिंटेक्स का उपयोग करते हैं।

swift
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

unowned self weak self का एक विकल्प है जब self के क्लोजर से अधिक जीवित रहने की गारंटी होती है। उदाहरण: सिंक्रोनस क्लोजर जो तुरंत निष्पादित होते हैं (sorted, filter)। ऐसे मामलों में self निश्चित रूप से जीवित है, और unowned सुरक्षित है। हालाँकि, unowned मुक्त ऑब्जेक्ट तक पहुँचने पर क्रैश करता है — इसलिए weak को डिफ़ॉल्ट सुरक्षित विकल्प माना जाता है।

Retain cycle कैसे पता लगाएं: निदान उपकरण

एप्लिकेशन प्रदर्शन के लिए प्रारंभिक चरण में retain cycle का पता लगाना अत्यंत महत्वपूर्ण है। आइए iOS डेवलपमेंट में चाक्रिक संदर्भों की पहचान के लिए मुख्य उपकरणों और तकनीकों की समीक्षा करें।

Xcode Memory Debugger

Xcode Memory Debugger (Debug Memory Graph) एक दृश्य उपकरण है जो मेमरी में ऑब्जेक्टों का ग्राफ उनके संदर्भों के साथ दिखाता है। एक retain cycle strong तीरों की एक बंद श्रृंखला के रूप में दिखाई देता है। लॉन्च करने के लिए: ऐप चलने के दौरान Debug area पैनल में Debug Memory Graph बटन पर क्लिक करें। प्रत्येक ऑब्जेक्ट अपने प्रकार, पता और संदर्भों की सूची के साथ दिखाया जाता है।

Instruments Leaks

Instruments Leaks स्वचालित लीक पता लगाने के लिए एक प्रोफाइलर है। यह आवंटन रिकॉर्ड करता है और वास्तविक समय में संदर्भ ग्राफ का विश्लेषण करता है। यह ना केवल retain cycle बल्कि भूले हुए संदर्भों, अमुक्त ViewController और अन्य लीक्स का भी पता लगाता है। Leaks सटीक ऑब्जेक्ट और धारण श्रृंखला को इंगित करता है।

Deinit लॉगिंग

सबसे सरल तरीका हर मुख्य क्लास के 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 रोकथाम और सबसे अच्छी प्रथाएँ

प्रोडक्शन में सुधारने की तुलना में retain cycle को रोकना आसान है। यहाँ कुछ नियम हैं जो चाक्रिक संदर्भों के जोखिम को कम से कम करते हैं।

Weak delegate नियम

सभी डेलीगेट और 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) में, strong संदर्भों की दिशा का ट्रैक करें। स्वामी अधीनस्थ पर strong संदर्भ रखता है, लेकिन अधीनस्थ को स्वामी का संदर्भ केवल weak या unowned के माध्यम से लेना चाहिए। एकदिशात्मक डेटा प्रवाह संदर्भ प्रबंधन को सरल बनाता है।

swift
// उदाहरण: 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 है। सभी ViewControllers के लिए इस अभ्यास को प्रोजेक्ट टेम्पलेट में जोड़ें।

अक्सर पूछे जाने वाले प्रश्न

Retain cycle GC में मेमरी लीक से कैसे अलग है?

Retain cycle एक विशिष्ट ARC समस्या है जहां strong संदर्भों का एक बंद लूप मुक्ति को अवरुद्ध करता है। GC में, संग्रहक रूट सेट से पहुँचने क्षमता का विश्लेषण करता है, संदर्भ गणनाओं का नहीं — इसलिए चक्र लीक नहीं हैं। ARC में, हालाँकि, कोई भी पृथक चक्र एक गारंटीशुदा लीक है।

Weak संदर्भ retain cycle कैसे तोड़ता है?

एक weak संदर्भ किसी ऑब्जेक्ट के retain count को नहीं बढ़ाता है। यदि आप एक चक्र में एक strong संदर्भ को weak से बदलते हैं, तो प्रत्येक ऑब्जेक्ट का retain count शून्य हो सकता है। ऑब्जेक्ट के मुक्त होने के बाद, weak संदर्भ स्वचालित रूप से nil पर सेट हो जाता है, जो मुक्त मेमरी तक पहुँचने से रोकता है।

क्या कोई retain cycle तीन या अधिक ऑब्जेक्टों से मिलकर बन सकता है?

हाँ, एक retain cycle किसी भी संख्या में ऑब्जेक्टों को शामिल कर सकता है: A → B → C → A। इसे तोड़ने के लिए, आपको केवल एक कड़ी को तोड़ने की आवश्यकता है — किसी भी strong संदर्भ को weak या unowned से बदलें। उपकरण पूरा ग्राफ दिखाते हैं, न केवल ऑब्जेक्टों के जोड़े।

GCD DispatchWorkItem retain cycle क्यों नहीं बनाता?

GCD (Grand Central Dispatch) निष्पादन के बाद क्लोजर को संग्रहीत नहीं रखता है। DispatchWorkItem निष्पादित होता है और जारी हो जाता है, भले ही क्लोजर self को कैप्चर करता हो। Retain cycle केवल तब होता है जब किसी क्लोजर को संपत्ति (क्लास में completion handler) के रूप में संग्रहीत किया जाता है, न कि जब इसे किसी क्यू में भेजा जाता है।

किस प्रकार के retain cycle Instruments द्वारा नहीं पता लगाए जाते?

Instruments Leaks हमेशा अस्थायी retain cycle (सेकंड तक चलने वाले) या ब्रिजिंग के माध्यम से C/C++ ऑब्जेक्टों में चाक्रिक संदर्भों का पता नहीं लगा पाता। पूर्ण जाँच के लिए, दृश्य में सभी मुख्य ऑब्जेक्टों के deinit लॉगिंग के साथ Memory Debugger का मैनुअल उपयोग करें।

सारांश

  • रिटेन साइकल — strong संदर्भों की एक बंद श्रृंखला जो ARC में ऑब्जेक्ट मुक्ति को अवरुद्ध करती है
  • कारण — strong संदर्भों वाले डेलीगेट, self को कैप्चर करने वाले क्लोजर, द्वि-दिशात्मक मात्र-पित्र संबंध
  • समाधान — एक strong संदर्भ को weak या unowned से बदलना चक्र को तोड़ता है
  • क्लोजर — संग्रहीत completion handlers को हमेशा [weak self] का उपयोग करना चाहिए
  • डेलीगेट — हमेशा weak; डेलीगेट प्रोटोकॉल को AnyObject से प्राप्त होना चाहिए
  • पता लगाना — Xcode Memory Debugger, Instruments Leaks, deinit लॉगिंग
  • रोकथाम — एकदिशात्मक डेटा प्रवाह, weak डेलीगेट, कैप्चर सूची, deinit के साथ बेस क्लास

हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे

IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।

परियोजना पर चर्चा करें

यह भी पढ़ें