एक विधि से नव निर्मित वस्तुओं को वापस करने के लिए ऑटोरेलिज पूल की आवश्यकता होती है। उदाहरण के लिए इस कोड के टुकड़े पर विचार करें:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
}
विधि में बनाई गई स्ट्रिंग में एक की एक बरकरार गणना होगी। अब कौन रिलीज के साथ गिनती बनाए रखेगा?
विधि ही? संभव नहीं है, इसे बनाई गई वस्तु को वापस करना है, इसलिए इसे लौटने से पहले इसे जारी नहीं करना चाहिए।
विधि का कॉलर? कॉल करने वाले को उस ऑब्जेक्ट को प्राप्त करने की उम्मीद नहीं है जिसे जारी करने की आवश्यकता है, विधि नाम का अर्थ यह नहीं है कि एक नया ऑब्जेक्ट बनाया गया है, यह केवल कहता है कि एक ऑब्जेक्ट वापस आ गया है और यह लौटी हुई वस्तु एक नया है जिसे रिलीज की आवश्यकता हो सकती है लेकिन ऐसा हो सकता है अच्छी तरह से एक मौजूदा है कि नहीं करता है। क्या विधि वापस आती है यह कुछ आंतरिक स्थिति पर भी निर्भर हो सकता है, इसलिए कॉल करने वाले को यह पता नहीं चल सकता है कि क्या उसे उस वस्तु को जारी करना है और उसकी देखभाल नहीं करनी चाहिए।
यदि फोन करने वाले को हमेशा कन्वेंशन द्वारा सभी लौटी हुई वस्तु को जारी करना होता है, तो नई बनाई गई प्रत्येक वस्तु को हमेशा इसे एक विधि से वापस करने से पहले बरकरार नहीं रखना होगा और इसे स्कोलर द्वारा तब तक जारी करना होगा, जब तक कि यह दायरे से बाहर न हो जाए यह फिर से लौटा है। यह कई मामलों में अत्यधिक अक्षम होगा क्योंकि एक व्यक्ति पूरी तरह से कई मामलों में रिटेन काउंट को बदलने से बच सकता है यदि कॉलर हमेशा लौटी हुई वस्तु को जारी नहीं करेगा।
यही कारण है कि ऑटोरेलिज पूल हैं, इसलिए पहली विधि वास्तव में बन जाएगी
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:@"Hello %@!", self.username];
return [res autorelease];
}
autorelease
किसी ऑब्जेक्ट पर कॉल करने से यह ऑटोरेलिज़ पूल में जुड़ जाता है, लेकिन ऑटोरलीज़ पूल में ऑब्जेक्ट को जोड़ने का वास्तव में क्या मतलब है? ठीक है, इसका मतलब है कि आपके सिस्टम को " मैं चाहता हूं कि आप मेरे लिए उस वस्तु को जारी करें, लेकिन कुछ समय बाद, अभी नहीं, इसकी एक प्रतिधारण संख्या है जिसे रिलीज से संतुलित करने की आवश्यकता है अन्यथा स्मृति रिसाव होगी लेकिन मैं खुद ऐसा नहीं कर सकता अभी मुझे अपने मौजूदा दायरे से परे रहने के लिए वस्तु की आवश्यकता है और मेरा कॉलर मेरे लिए ऐसा नहीं करेगा, उसे इस बात की कोई जानकारी नहीं है कि यह करने की आवश्यकता है। इसलिए इसे अपने पूल में जोड़ें और एक बार जब आप इसे साफ कर लें। पूल, मेरे लिए मेरी वस्तु को भी साफ करें। "
एआरसी के साथ कंपाइलर आपके लिए यह तय करता है कि किसी ऑब्जेक्ट को कब बनाए रखा जाए, कब किसी ऑब्जेक्ट को रिलीज किया जाए और कब इसे ऑटोरेलिज पूल में जोड़ा जाए लेकिन मेमोरी को लीक करने के तरीकों से नए बनाए गए ऑब्जेक्ट्स को वापस करने में सक्षम होने के लिए ऑटोरेलिज पूल की उपस्थिति की आवश्यकता होती है। Apple ने उत्पन्न कोड के लिए कुछ निफ्टी अनुकूलन किए हैं जो कभी-कभी रनटाइम के दौरान ऑटोरेलिज पूल को खत्म कर देगा। इन अनुकूलन के लिए आवश्यक है कि कॉल करने वाले और कॉल करने वाले दोनों एआरसी का उपयोग कर रहे हों (याद रखें कि एआरसी और गैर-एआरसी का मिश्रण कानूनी है और आधिकारिक रूप से समर्थित भी है) और यदि वास्तव में मामला केवल रनटाइम पर जाना जा सकता है।
इस एआरसी कोड पर विचार करें:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
कोड जो सिस्टम उत्पन्न करता है, वह या तो निम्न कोड की तरह व्यवहार कर सकता है (वह सुरक्षित संस्करण है जो आपको एआरसी और गैर-एआरसी कोड को स्वतंत्र रूप से मिलाने की अनुमति देता है):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(ध्यान दें कि कॉलर में जारी / जारी करना केवल एक रक्षात्मक सुरक्षा बनाए रखना है, इसकी कड़ाई से आवश्यकता नहीं है, कोड इसके बिना पूरी तरह से सही होगा)
या यह इस कोड की तरह व्यवहार कर सकता है, जब दोनों को रनटाइम पर ARC का उपयोग करने का पता चलता है:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
जैसा कि आप देख सकते हैं, ऐप्पल ने एटॉर्लीज को समाप्त कर दिया है, इस प्रकार पूल को नष्ट होने पर विलंबित ऑब्जेक्ट रिलीज़, साथ ही साथ सुरक्षा बरकरार रहती है। यह कैसे संभव है और क्या वास्तव में पर्दे के पीछे चल रहा है, इसके बारे में अधिक जानने के लिए, इस इस ब्लॉग पोस्ट को देखें।
अब वास्तविक प्रश्न के लिए: कोई क्यों उपयोग करेगा @autoreleasepool
?
अधिकांश डेवलपर्स के लिए, उनके कोड में इस निर्माण का उपयोग करने के लिए आज केवल एक ही कारण बचा है और वह यह है कि जहां लागू हो वहां मेमोरी फुटप्रिंट को छोटा रखें। जैसे इस लूप पर विचार करें:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
मान लें कि हर कॉल tempObjectForData
एक नया बना सकता हैTempObject
जिसे ऑटोरेलिज़ वापस किया जाता है। फॉर-लूप इन अस्थायी वस्तुओं में से एक मिलियन का निर्माण करेगा जो सभी वर्तमान ऑटोरेलिस्पुल में एकत्र किए जाते हैं और केवल एक बार उस पूल को नष्ट कर दिया जाता है, सभी टेम्प ऑब्जेक्ट्स भी नष्ट हो जाते हैं। जब तक ऐसा नहीं होता, तब तक आपके पास स्मृति में इन अस्थायी वस्तुओं में से एक मिलियन हैं।
यदि आप इसके बजाय इस तरह कोड लिखते हैं:
for (int i = 0; i < 1000000; i++) @autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
फिर एक नया पूल हर बार फॉर-लूप चलता है और प्रत्येक लूप पुनरावृत्ति के अंत में नष्ट हो जाता है। लूप के एक लाख बार चलने के बावजूद किसी भी समय मेमोरी में लगभग एक ही अस्थायी ऑब्जेक्ट लटका हुआ है।
अतीत में आपको अक्सर थ्रेड का प्रबंधन करते समय स्वयं को ऑटोरेलिस्पुलस भी प्रबंधित करना पड़ता था (उदाहरण के लिए NSThread
) केवल मुख्य धागे के रूप में एक कोको / यूकीट ऐप के लिए ऑटोरेलिज पूल होता है। फिर भी यह आज बहुत अधिक विरासत है क्योंकि आज आप शायद शुरू करने के लिए धागे का उपयोग नहीं करेंगे। आप GCD DispatchQueue
के या NSOperationQueue
इन दोनों का उपयोग करेंगे और ये दोनों आपके लिए एक शीर्ष स्तर के ऑटोरेलिज़ पूल का प्रबंधन करेंगे, जो एक ब्लॉक / कार्य को चलाने से पहले बनाया गया था और एक बार इसके साथ नष्ट हो गया था।