2012-08-15 21 views
9

पर कॉल किया गया है, मैंने स्टैक ओवरफ़्लो पर बहुत सी पोस्ट देखी हैं, यह देखते हुए कि नियंत्रकों की दृश्यडिडलोड विधि को केवल पहली बार नियंत्रक तक पहुंचाया जाता है और हर बार जरूरी नहीं है कम से कम एक बार।viewDidLoad वास्तव में प्रत्येक बार एक segue संक्रमण

यह वही नहीं है जो मैं देख रहा हूं! मैंने इसे हाइलाइट करने के लिए एक सरल परीक्षण रखा: https://github.com/imuz/ViewDidLoadTest

यह नेविगेशन नियंत्रक सेग्यूज़ और मोडल व्यू व्यू के लिए लगता हैडिडलोड हमेशा कहा जाता है। टैब के बीच स्विच करते समय केवल एक बार कॉल नहीं किया जाता है।

मैं विपरीत इस पा सकते हैं viewDidLoad का हर विवरण:

और सेब स्वयं के प्रलेखन संकेत मिलता है कि एक दृश्य केवल उतार दिया जाता है जब स्मृति कम होने पर ।

मैं वर्तमान में दृश्य में प्रारंभ कर रहा हूं डीडलोड यह धारणा बना रहा है कि इसे प्रत्येक सीगू संक्रमण के साथ बुलाया जाता है।

क्या मुझे यहां कुछ याद आ रही है?

उत्तर

11

मुझे विश्वास है कि ऐप्पल दस्तावेज एक ऐसी स्थिति का वर्णन कर रहा है जहां दृश्य नियंत्रक को हटाया नहीं जा रहा है। यदि आप एक सेग्यू का उपयोग करते हैं, तो आप एक नए गंतव्य नियंत्रक के त्वरण का कारण बन रहे हैं और, एक नई वस्तु होने के कारण, इसे एक दृश्य लोड करने की आवश्यकता है।

xib- आधारित ऐप्स में, मैंने कभी-कभी नियंत्रक ऑब्जेक्ट को कैश किया है जो मुझे पता था कि मैं बार-बार उपयोग कर सकता हूं। उन मामलों में, जब दस्तावेज़ को लोड किया जाना था, तो वे दस्तावेज़ीकरण को ध्यान में रखते हुए व्यवहार करते थे।

संपादित करें: आपके द्वारा शामिल लिंक पढ़ने पर, मुझे उनमें कोई विरोधाभास दिखाई नहीं देता है। वे भी उन चीजों के बारे में बात कर रहे हैं जो दृश्य नियंत्रक वस्तु के जीवनकाल के दौरान होते हैं।

+0

यह बिट्स जहां वे 'कम स्मृति स्थितियों' पर अनलोड किए गए विचारों के बारे में बात करते हैं, जिसका अर्थ यह है कि वे डिफ़ॉल्ट रूप से चारों ओर छोड़ दिए जाते हैं जिन्हें मैंने नहीं देखा है। तो वास्तव में यह माता-पिता नियंत्रक कार्यान्वयन पर निर्भर करता है। यदि आप नेविगेशन नियंत्रकों का उपयोग करते हैं तो हर बार एक नया उदाहरण बनाया जाता है और लोड किया जाता है। टैब कंट्रोलर के साथ ऐसा नहीं है। – Imran

+1

कम मेमोरी व्यवहार (सिम्युलेटर पर) का परीक्षण करने का एक तरीका एक व्यू कंट्रोलर रखना है, इसे एक मोडल व्यू कंट्रोलर के साथ कवर करना है, और हार्डवेयर-> मेमोरी चेतावनी विकल्प अनुकरण करें। छुपा नियंत्रक का दृश्य अनलोड करना चाहिए, और फिर मोडल को खारिज करते समय फिर से लोड करना चाहिए। –

+0

ओआईसी दिलचस्प, बीमार कोशिश करो। मुझे लगता है कि वर्तमान में सक्रिय नहीं है कि कोई भी दृश्य उतारने के लिए एक उम्मीदवार है। – Imran

0

इसे हर बार कंट्रोलर का दृश्य स्क्रैच से लोड किया जाता है (यानी अनुरोध किया गया है लेकिन अभी तक उपलब्ध नहीं है)। यदि आप नियंत्रक को हटा देते हैं और दृश्य इसके साथ जाता है, तो अगली बार जब आप नियंत्रक को तुरंत चालू करेंगे तो इसे फिर से कॉल किया जाएगा (उदाहरण के लिए जब आप नियंत्रक को इसे सामान्य रूप से या segue के माध्यम से पुश करने के लिए बनाते हैं)। टैब में नियंत्रक को हटाया नहीं जाता है क्योंकि टैब नियंत्रक उन्हें चारों ओर रखता है।

12

फिलिप मिल्स का जवाब सही है। यह सिर्फ इसका एक संवर्द्धन है।

सिस्टम दस्तावेज के रूप में काम कर रहा है।

आप viewDidLoad देख रहे हैं क्योंकि दृश्य नियंत्रक नेविगेशन नियंत्रक पर धक्का दिया जा रहा है नया उदाहरण है। यह कॉल व्यूडिडलोड होना चाहिए।

यदि आप थोड़ा आगे की जांच करते हैं, तो आप देखेंगे कि उनमें से प्रत्येक दृश्य नियंत्रक को पॉप किए जाने पर हटा दिया जाता है (केवल ब्रेकपॉइंट या एनएसएलओजी को डेलोक में डालें)। इस डीलोकेशन में व्यू कंट्रोलर कंटेनर के साथ कुछ लेना देना नहीं है ... यह नियंत्रक के जीवन को नियंत्रित नहीं करता है ... यह सिर्फ इसके लिए एक मजबूत संदर्भ रखता है।

जब नियंत्रक नेविगेशन नियंत्रक स्टैक से पॉप आउट हो जाता है, तो एनएवी नियंत्रक इसके संदर्भ को जारी करता है, और चूंकि इसके कोई अन्य संदर्भ नहीं हैं, तो दृश्य नियंत्रक विलुप्त हो जाएगा।

नेविगेशन नियंत्रक केवल अपने सक्रिय ढेर में नियंत्रकों को देखने के लिए मजबूत संदर्भ रखता है।

यदि आप एक ही नियंत्रक का पुन: उपयोग करना चाहते हैं, तो आप इसका पुन: उपयोग करने के लिए ज़िम्मेदार हैं। जब आप स्टोरीबोर्ड सेग का उपयोग करते हैं, तो आप उस नियंत्रण को छोड़ देते हैं (काफी हद तक)।

मान लें कि आपके पास सीगू नियंत्रक Foo को कुछ बटन टैप करने के परिणामस्वरूप देखने के लिए है। जब वह बटन टैप किया जाता है, तो "सिस्टम" Foo (गंतव्य दृश्य नियंत्रक) का एक उदाहरण बना देगा, और फिर segue निष्पादित करेगा। नियंत्रक कंटेनर अब उस दृश्य नियंत्रक के लिए एकमात्र मजबूत संदर्भ रखता है। एक बार यह करने के बाद, वीसी dealloc जाएगा।

चूंकि यह प्रत्येक बार एक नया नियंत्रक बनाता है, viewDidLoad प्रत्येक बार नियंत्रक प्रस्तुत किया जाएगा।

अब, यदि आप इस व्यवहार को बदलना चाहते हैं, और बाद में पुन: उपयोग के लिए व्यू कंट्रोलर को कैश करें, तो आपको इसे विशेष रूप से करना होगा। यदि आप स्टोरीबोर्ड सेग का उपयोग नहीं करते हैं, तो यह आसान है क्योंकि आप वास्तव में वीवी को एनवी नियंत्रक को धक्का दे रहे हैं।

यदि, हालांकि, आप स्टोरीबोर्ड सेग का उपयोग करते हैं, तो यह थोड़ा और परेशानी है।

ऐसा करने के कई तरीके हैं, लेकिन सभी को हैकिंग के कुछ रूप की आवश्यकता है। स्टोरीबोर्ड स्वयं नए दृश्य नियंत्रकों को तुरंत चालू करने का प्रभारी है। एक तरीका है instantiateViewControllerWithIdentifier ओवरराइड करना। यही वह तरीका है जिसे एक सेग्यू को व्यू कंट्रोलर बनाने की आवश्यकता होती है। इसे नियंत्रकों के लिए भी कहा जाता है कि आप पहचानकर्ता नहीं देते हैं (यदि आप एक असाइन नहीं करते हैं तो सिस्टम एक अद्वितीय अपीलकर्ता प्रदान करता है)।

नोट, मुझे आशा है कि यह ज्यादातर शैक्षिक उद्देश्यों के लिए है। मैं निश्चित रूप से यह आपकी समस्याओं को हल करने का सबसे अच्छा तरीका नहीं बता रहा हूं, जो कुछ भी हो सकता है।

कुछ की तरह ...

@interface MyStoryboard : UIStoryboard 
@property BOOL shouldUseCache; 
- (void)evict:(NSString*)identifier; 
- (void)purge; 
@end 
@implementation MyStoryboard 
- (NSMutableDictionary*)cache { 
    static char const kCacheKey[1]; 
    NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey); 
    if (nil == cache) { 
     cache = [NSMutableDictionary dictionary]; 
     objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN); 
    } 
    return cache; 
} 
- (void)evict:(NSString *)identifier { 
    [[self cache] removeObjectForKey:identifier]; 
} 
- (void)purge { 
    [[self cache] removeAllObjects]; 
} 
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier { 
    if (!self.shouldUseCache) { 
     return [super instantiateViewControllerWithIdentifier:identifier]; 
    } 
    NSMutableDictionary *cache = [self cache]; 
    id result = [cache objectForKey:identifier]; 
    if (result) return result; 
    result = [super instantiateViewControllerWithIdentifier:identifier]; 
    [cache setObject:result forKey:identifier]; 
    return result; 
} 
@end 

अब आप इस स्टोरीबोर्ड का उपयोग करना होगा। दुर्भाग्यवश, जबकि यूआईएप्लिकेशंस मुख्य स्टोरीबोर्ड पर रखता है, यह इसे पाने के लिए एपीआई का पर्दाफाश नहीं करता है। हालांकि, प्रत्येक व्यू कंट्रोलर के पास एक स्टोरीबोर्ड प्राप्त करने के लिए storyboard है, जिसे से बनाया गया था।

यदि आप अपने स्टोरीबोर्ड लोड कर रहे हैं, तो बस MyStoryboard को तुरंत चालू करें। यदि आप डिफ़ॉल्ट स्टोरीबोर्ड का उपयोग कर रहे हैं, तो आपको सिस्टम को अपने विशेष उपयोग के लिए मजबूर करने की आवश्यकता है। फिर, ऐसा करने के कई तरीके हैं। दृश्य नियंत्रक में स्टोरीबोर्ड एक्सेसर विधि को ओवरराइड करना एक आसान तरीका है।

आप MyStoryboard को प्रॉक्सी क्लास बना सकते हैं जो आगे सब कुछ UIStoryboard पर कर सकता है, या आप मुख्य स्टोरीबोर्ड को आईएसए-स्विजल कर सकते हैं, या आप अपने स्थानीय नियंत्रक को अपनी स्टोरीबोर्ड विधि से वापस कर सकते हैं।

अब, याद रखें, यहां कोई समस्या है। क्या होगा यदि आप एक ही दृश्य नियंत्रक को स्टैक पर एक से अधिक बार धक्का देते हैं? कैश के साथ, सटीक समान दृश्य नियंत्रक ऑब्जेक्ट का उपयोग कई बार किया जाएगा। क्या आप वास्तव में यही चाहते हैं?

यदि नहीं, तो अब आपको नियंत्रक कंटेनरों के साथ बातचीत का प्रबंधन करने की आवश्यकता है ताकि वे यह देखने के लिए जांच सकें कि यह नियंत्रक पहले से ही उनके द्वारा ज्ञात है या नहीं, इस मामले में एक नया उदाहरण आवश्यक है।

तो, वहाँ एक तरह से डिफ़ॉल्ट स्टोरीबोर्ड segues (वास्तव में वहाँ काफी कुछ तरीके हैं) का उपयोग करते समय कैश की गई नियंत्रक प्राप्त करने के लिए है ... लेकिन यह है कि नहीं एक अच्छी बात जरूरी नहीं है, और निश्चित रूप से आप डिफ़ॉल्ट रूप से क्या मिलेगा ।

+0

विस्तृत उत्तर के लिए धन्यवाद। मैं नियंत्रकों को कैशिंग नहीं के साथ ठीक हूँ। मैं सिर्फ लोड अनलोड चक्र को थोड़ा और समझना चाहता था, इसलिए मैं समझ सकता था कि मैं क्या कर सकता हूं और दृश्य में नहीं डाल सकता। अब यह बहुत स्पष्ट है। – Imran

+1

असल में, viewWillUnload और viewDidUnload को बहिष्कृत कर दिया गया है, इसलिए आपको आगे बढ़ने में कोई भी कोड नहीं डालना चाहिए। हैंडल किया गया ReceiveMemory चेतावनी स्मृति के तहत संसाधनों को मुक्त करने के लिए। –

+0

आह आईओएस 6 में सही है। लगता है कि अब डिडलोड को देखने के लिए कोई वास्तविक बिंदु नहीं है, यह एक कन्स्ट्रक्टर भी हो सकता है। मैं इसे लेता हूं इसका मतलब है कि मेमोरी चेतावनियां अब दृश्य को लोड नहीं करतीं? इस प्रकार की चीजें बदलती हैं क्योंकि इसका मतलब है किडिडलोड केवल एक बार कभी भी बुलाया जाता है? परीक्षण के समय मेरे पास आईओएस 6 नहीं है। – Imran