शाश्वत दुविधा: [unowned self] लगाएं या नहीं?
\n\nयदि आपने कभी Swift में लिखा है और closures का सामना किया है, तो आपने निश्चित रूप से यह प्रश्न पूछा होगा: "क्या मुझे प्रत्येक closure के अंदर [unowned self] लिखना चाहिए जहाँ मैं self का उपयोग करता हूँ?"
यह Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है (दसियों हज़ार व्यूज़), और यह सही भी है। 2025 में, Swift की नई सुविधाओं (जैसे बेहतर जीवनचक्र विश्लेषक और कंपाइलर की अधिक कठोर जाँच) के आगमन के साथ, उत्तर और भी अधिक सूक्ष्म हो गया है।
\n\nइस लेख में, हम केवल "हाँ" या "नहीं" का सूखा उत्तर नहीं देंगे। हम विश्लेषण करेंगे कि Swift में मेमोरी प्रबंधन वास्तव में कैसे काम करता है, कब [unowned self] एक वरदान है, और कब यह एक टाइम बम है, और आधुनिक प्रोजेक्ट्स में किन सर्वोत्तम प्रथाओं का उपयोग करना चाहिए।
चलिए शुरू करते हैं! 🚀
\n\nARC और retain cycles के बारे में संक्षेप में (शुरुआती लोगों के लिए)
\n\n[unowned self] के बारे में बात करने से पहले, आइए याद करें कि Automatic Reference Counting (ARC) कैसे काम करता है।
ARC एक संदर्भ गणना तंत्र है। Swift में प्रत्येक ऑब्जेक्ट (क्लास का इंस्टेंस) में एक संदर्भ काउंटर होता है। जब आप ऑब्जेक्ट पर एक नया संदर्भ बनाते हैं (उदाहरण के लिए, इसे किसी प्रॉपर्टी को असाइन करते हैं या किसी ऐरे में डालते हैं), तो काउंटर बढ़ जाता है। जब संदर्भ गायब हो जाता है, तो यह घट जाता है। जब काउंटर शून्य हो जाता है, तो ऑब्जेक्ट नष्ट हो जाता है।
\n\nसमस्या तब उत्पन्न होती है जब दो ऑब्जेक्ट एक-दूसरे को संदर्भित करते हैं (चक्रीय संदर्भ)। कल्पना करें:
\n\nclass Parent {\n var child: Child?\n}\n\nclass Child {\n var parent: Parent? // Parent पर वापस मजबूत संदर्भ\n}\n\nयदि आप Parent और Child की एक जोड़ी बनाते हैं और दोनों संदर्भ सेट करते हैं, तो वे हमेशा के लिए एक-दूसरे को पकड़े रहेंगे। ARC कभी भी उनके काउंटर को शून्य नहीं करेगा। इसे retain cycle (मजबूत संदर्भों का चक्र) कहा जाता है।
Closures भी संदर्भ प्रकार हैं। जब कोई closure self को कैप्चर करता है (उदाहरण के लिए, किसी विधि को कॉल करने के लिए), तो यह ऑब्जेक्ट पर एक मजबूत संदर्भ संग्रहीत करता है। यदि ऑब्जेक्ट, बदले में, closure पर एक मजबूत संदर्भ संग्रहीत करता है (उदाहरण के लिए, एक प्रॉपर्टी के रूप में), तो वही चक्र उत्पन्न होता है।
Weak बनाम Unowned: क्या अंतर है?
\n\nचक्र को तोड़ने के लिए, हम weak या unowned का उपयोग करते हैं।
weak self
\n- \n
- हमेशा optional (
self?)। \n - संदर्भ ARC काउंटर को नहीं बढ़ाता। \n
- जब ऑब्जेक्ट नष्ट हो जाता है, तो
weakसंदर्भ स्वचालित रूप सेnilहो जाता है। \n - सुरक्षित — आप हमेशा
guard let self = self else { return }की जाँच करते हैं। \n
unowned self
\n- \n
- Optional नहीं (प्रश्न चिह्न के बिना
self)। \n - संदर्भ ARC काउंटर को नहीं बढ़ाता। \n
- जब ऑब्जेक्ट नष्ट हो जाता है, तो
unownedसंदर्भ nil नहीं होता। यह एक "लटकता हुआ पॉइंटर" (dangling pointer) बन जाता है। \n - ऑब्जेक्ट के नष्ट होने के बाद ऐसे संदर्भ तक पहुँचने से क्रैश (EXC_BAD_ACCESS) होता है। \n
मुख्य बिंदु: unowned कंपाइलर से एक वादा है: "मैं गारंटी देता हूँ कि यह closure निष्पादित होने तक ऑब्जेक्ट जीवित रहेगा।" यदि आप गलती करते हैं, तो एप्लिकेशन क्रैश हो जाएगा।
[unowned self] का उपयोग कब करें (और कब नहीं)
\n\nआइए 2025 में iOS डेवलपमेंट अभ्यास से वास्तविक परिदृश्यों का विश्लेषण करें।
\n\nकेस 1: [unowned self] — ठीक है (और यहाँ तक कि बेहतर भी)
\n\n[unowned self] का उपयोग करें जब आप पूरी तरह से आश्वस्त हों कि closure के निष्पादन के समय self जीवित रहेगा। एक विशिष्ट उदाहरण एक closure है जो ऑब्जेक्ट की विधि के अंदर समकालिक रूप से निष्पादित होता है, और ऑब्जेक्ट के closure के पूरा होने से पहले नष्ट न होने की गारंटी है।
class DataFetcher {\n var data: Data?\n \n func fetchData() {\n // यह closure विधि के अंदर समकालिक रूप से निष्पादित होता है\n let processCl