2009-03-03 12 views
15

में सामना की गई EXC_BAD_ACCESS त्रुटियों को कैसे हल करूं? मैं एक साधारण चीज़ करने की कोशिश कर रहा हूं; इंटरनेट से एक छवि पढ़ें, इसे आईफोन पर ऐप की दस्तावेज़ निर्देशिका में सहेजें, और उसे उस फ़ाइल से वापस पढ़ें ताकि मैं बाद में इसके साथ अन्य चीजें कर सकूं। फ़ाइल लिखना ठीक काम करता है लेकिन जब मैं इसे वापस पढ़ने की कोशिश करता हूं तो मुझे जीडीबी में एक EXC_BAD_ACCESS त्रुटि मिलती है जो मुझे नहीं पता कि कैसे हल किया जाए। कोडमैं आईफोन विकास

-(UIImage *) downloadImageToFile { 
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

NSString *documentsDirectory = [paths objectAtIndex:0]; 

[paths release] 
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

[data writeToFile:path atomically:YES]; 

return [[UIImage alloc] initWithContentsOfFile:path]; 
} 

वापसी बयान में विफल रहता है जब मैं फ़ाइल से UIImage प्रारंभ करने की कोशिश: यहाँ की तरह मेरे कोड मूल रूप से दिखाई देता है। कोई विचार?

संपादित करें: मूल रूप से कोड में समस्या को रिलीज़ करने के लिए उपेक्षित।

उत्तर

8

आपका कोड कैसे स्मृति प्रबंधन ऑब्जेक्टिव-सी में काम करता है के ज्ञान की एक गंभीर कमी को दर्शाता है। EXC_BAD_ACCESS त्रुटियों आपको प्राप्त हो रही के अलावा, अनुचित स्मृति प्रबंधन भी मेमोरी लीक जो, iPhone की तरह एक छोटा सा उपकरण पर, यादृच्छिक दुर्घटनाओं को जन्म दे सकता का कारण बनता है।

मेरा सुझाव है कि आप इस एक पढ़ा thorogh दे:

Introduction to Memory Management Programming Guide for Cocoa

+0

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

+0

कोई समस्या नहीं, उम्मीद है कि मैं बहुत कठोर नहीं था। वह दस्तावेज पढ़ने के लायक है। आपके विकास के साथ कामयाबी की शुभकामना! – August

+2

मुझे हाल ही में EXC_BAD_ACCESS के बारे में बहुत सारे प्रश्न मिल रहे हैं इसलिए मैंने इसे http://loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html लिखा है, इसे डिबगिंग तकनीकों की एक सूची के साथ। इस मामले में (बहुत से रिलीज), बिल्ड और विश्लेषण/स्कैन-बिल्ड शायद इसे ध्वजांकित कर दिया होगा। यदि नहीं, तो लाश को निश्चित रूप से सक्षम करना होगा। –

0

यह त्रुटि तब होती है जब आप स्मृति mismanaging कर रहे हैं (यानी। एक वस्तु समय से पहले ही जारी किया जा रहा है या समान)

प्रयास करें निम्नलिखित ..

UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path]; 
return [myImage autorelease]; 

मैं बहुत समय बिताया प्रयोग की तरह कुछ कर रही रिलीज/autorelease की अवधारणाओं के साथ पकड़ने के दौरान। कभी कभी कीवर्ड को बनाए रखने के भी खेला जाना है (हालांकि शायद इस मामले में नहीं) की जरूरत है

एक अन्य विकल्प हो सकता है बस पथ मौजूद नहीं है, या से पढ़ा नहीं जा सकता?

+0

मुझे पता है कि यह मौजूद है, क्योंकि मैं फ़ाइल बना रहा हूं और यह फाइल सिस्टम में है। मैं स्मृति प्रबंधन चीजों के साथ कुछ और खेलूँगा यह देखने के लिए कि मैं क्या पा सकता हूं। – Kevlar

+0

पथ से स्लैश लें, और सुनिश्चित करें कि यह प्रोजेक्ट में है। इससे कोई फर्क नहीं पड़ता कि यह उस डीआईआर में है, लेकिन इसे एक्सेस करने के लिए इसे प्रोजेक्ट में जोड़ा जाना है। – Genericrich

+0

यह गलत है - आपको लौटाए गए मूल्य को ऑटोरेलेज़ करना चाहिए, लेकिन आपको कक्षा के आवृत्ति चर के रूप में नहीं रखना चाहिए। –

-1

शायद, initWithContentsOfFile एक रास्ता तर्क नहीं ले करता है? UIImage के लिए विभिन्न init विधियों पर चारों ओर ब्राउज़ करें, मुझे लगता है कि पथ स्वीकार करने के लिए एक अलग है।

वहाँ

भी कुछ अधिक सजावटी एक रास्ता बनाने के लिए क्या करना है हो सकता है? मुझे "बंडल" के साथ कुछ करना याद है? बहुत अस्पष्ट होने के लिए खेद है, यह सब मुझे याद है।

-2

मार्ग से हटकर स्लेश लेते हैं, और इस परियोजना में है सुनिश्चित करें। इससे कोई फर्क नहीं पड़ता कि यह उस डीआईआर में है, लेकिन इसे एक्सेस करने के लिए इसे प्रोजेक्ट में जोड़ा जाना है।

+0

मैं प्रोजेक्ट में फ़ाइल नहीं जोड़ सकता क्योंकि यह प्रोग्राम चलाने से पहले नहीं है; मैं इसे कहीं और पढ़ने के बाद फ़ाइल को सहेज रहा हूं। – Kevlar

+0

एनएसएसटींग की फाइल सिस्टम पथ मैनिपुलेशन विधियों का उपयोग करें और आपको इस बारे में चिंता करने की आवश्यकता नहीं होगी कि आपको स्लैश शामिल करना चाहिए या नहीं। हालांकि, यह इस समस्या से संबंधित नहीं है। –

1

निश्चित रूप से देना स्मृति प्रबंधन एक त्वरित समीक्षा नियम। कुछ भी नहीं निकलता है जो आपको प्राप्त होने वाली त्रुटि का कारण बनता है, लेकिन आप उन सभी ऑब्जेक्ट्स को अपने आवंटित कर रहे हैं। यदि आप बनाए रखने/रिलीज पैटर्न को समझ नहीं पाते हैं, संभावना है कि आपके कोड में एक और स्थान है जहां आप ऑब्जेक्ट को ठीक से नहीं बनाए रखते हैं, और यही वजह है कि EXC_BAD_ACCESS त्रुटि उत्पन्न होती है।

भी ध्यान रखें कि NSString फाइल सिस्टम पथ के साथ काम करने के लिए तरीके हैं, तो आप विभाजक अपने बारे में चिंता करने की ज़रूरत कभी नहीं करना चाहिए।

10

एक बात है कि मुझे एक बहुत मदद करता है objc_exception_throw पर एक ब्रेकपाइंट है। जब भी मैं एक अपवाद फेंकने वाला हूं, मैंने इस ब्रेकपॉइंट को मारा और मैं स्टैक चेन का बैक अप ले सकता हूं। मैं बस अपने आईफोन परियोजनाओं में इस ब्रेकपॉइंट को सक्षम करता हूं।

ऐसा करने के लिए, xcode में बाएं फलक के नीचे "समूह & फ़ाइलें" के पास जाएं और "ब्रेकपॉइंट्स" खोजें। इसे खोलें और प्रोजेक्ट ब्रेकपॉइंट्स पर और विस्तार फलक (शीर्ष) पर क्लिक करें, आपको "प्रतीक के लिए डबल-क्लिक" लेबल वाला एक नीला फ़ील्ड दिखाई देगा। उस पर डबल-क्लिक करें और "objc_exception_throw" दर्ज करें।

अगली बार जब आप अपवाद फेंकते हैं, तो आप रुकेंगे और डीबगर में, आप अपने कोड में स्टैक चेन का बैक अप ले सकते हैं जो अपवाद का कारण बनता है।

+0

धन्यवाद, यह एक बहुत ही उपयोगी युक्ति है। –

+0

मुझे लगता है कि यह अब एक्सकोड में चलाया गया है, रन मेनू में, "उद्देश्य-सी अपवादों पर रोकें" –

1

सामान्य रूप से, यदि आपको अपने कोड में EXC_BAD_ACCESSs मिल रही हैं और आप अपने जीवन के लिए नहीं समझ सकते हैं, क्यों NSZombie (नहीं, मैं मजाक नहीं कर रहा हूं) का उपयोग करने का प्रयास करें।

एक्सकोड में, बाईं ओर निष्पादन योग्य अनुभाग का विस्तार करें। उस लिस्टिंग पर डबल क्लिक करें जिसमें आपके प्रोजेक्ट के समान नाम है (यह केवल एक होना चाहिए)। पॉप अप करने वाली विंडो में, तर्क पर जाएं, और नीचे के हिस्से में, प्लस बटन पर क्लिक करें। नाम NSZombieEnabled होना चाहिए और मूल्य हाँ

इस तरह सेट किया जाना चाहिए, जब आप एक जारी की वस्तु तक पहुँचने का प्रयास है, तो आप आप क्या कर रहे का एक बेहतर होगा। एक बार जब आप बग पता लगाएंगे तो मूल्य को NO पर सेट करें।

उम्मीद है कि यह किसी की मदद करेगा!

46

नोट: यह गैर एआरसी स्मृति प्रबंधन के लिए विशेष रूप से लागू होता है।

चूंकि इसमें बहुत सारे विचार हैं और चेक किए गए उत्तर उचित रूप से बताते हैं कि "कोड उद्देश्य-सी में स्मृति प्रबंधन कैसे काम करता है, इस बारे में ज्ञान की गंभीर कमी दिखाता है," फिर भी किसी ने विशिष्ट त्रुटियों को इंगित नहीं किया है, मुझे लगता है मैं एक जवाब जोड़ूंगा जो उन पर छूएगा।

आधारभूत स्तर के नियम हम बुला तरीकों के बारे में याद करने के लिए है:

  • विधि कॉल शब्द alloc, नई, कॉपी, या बनाए रखने शामिल है, तो हम बनाई गई वस्तु का स्वामित्व है। ¹ अगर हमारे पास किसी ऑब्जेक्ट का स्वामित्व है, तो इसे जारी करने की हमारी ज़िम्मेदारी है।

  • यदि विधि कॉल में कोई शब्द नहीं है, तो हमारे पास बनाए गए ऑब्जेक्ट का स्वामित्व नहीं है। ¹ यदि हम पर किसी ऑब्जेक्ट का स्वामित्व नहीं है, तो इसे हमारी ज़िम्मेदारी जारी नहीं है, और इसलिए हमें इसे कभी नहीं करना चाहिए।

की प्रत्येक पंक्ति में ओपी के कोड देखें:

-(UIImage *) downloadImageToFile { 

हम एक नई विधि शुरू कर दिया। ऐसा करने में हमने एक नया संदर्भ शुरू कर दिया है जिसमें प्रत्येक निर्मित वस्तुएं रहती हैं। इसे थोड़ा सा ध्यान में रखें।अगली पंक्ति:

NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

हम url ही: शब्द alloc वहाँ हमें बताता है कि हम वस्तु का स्वामित्व है, और हम इसे अपने आप को रिलीज करने के लिए की आवश्यकता होगी कि। अगर हम नहीं करते हैं तो कोड स्मृति को रिसाव करेगा।

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

हम ही नहीं paths: चार जादुई शब्द का कोई उपयोग नहीं है, तो हम स्वामित्व की जरूरत नहीं है और यह अपने आप को कभी नहीं जारी करना होगा। कोई जादुई शब्द = कोई स्वामित्व:

NSString *documentsDirectory = [paths objectAtIndex:0]; 

हम documentsDirectory स्वामी नहीं हैं।

[paths release] 

वापस लाइनों के एक जोड़े हम देखते हैं कि हम रास्तों के स्वामी नहीं हैं जा रहे हैं, तो यह रिलीज एक EXC_BAD_ACCESS दुर्घटना का कारण के रूप में हम कुछ अब मौजूद नहीं है का उपयोग करने की कोशिश करेंगे। कोई जादुई शब्द = कोई स्वामित्व:

NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

हम path स्वामी नहीं हैं। alloc शब्द उपयोग है कि हम वस्तु का स्वामित्व है, वहाँ बताता है और हम इसे अपने आप को रिलीज करने के लिए की आवश्यकता होगी कि:

NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

हम data के मालिक हैं। अगर हम नहीं करते हैं तो कोड स्मृति को रिसाव करेगा।

निम्नलिखित दो पंक्तियां कुछ भी नहीं बनाती हैं या रिलीज़ नहीं करती हैं। फिर अंतिम पंक्ति आती है:

} 

विधि खत्म हो गई है, इसलिए चर के लिए संदर्भ समाप्त हो गया है। कोड को देखते हुए हम देख सकते हैं कि हमारे पास url और data दोनों का स्वामित्व है, लेकिन उनमें से कोई भी रिलीज़ नहीं हुआ। नतीजतन जब भी इस विधि को बुलाया जाता है तो हमारा कोड स्मृति को रिसाव करेगा।

NSURL ऑब्जेक्ट url बहुत बड़ा नहीं है, इसलिए यह संभव है कि हम कभी भी रिसाव को नोटिस न करें, हालांकि इसे अभी भी साफ किया जाना चाहिए, इसे रिसाव करने का कोई कारण नहीं है।

NSData ऑब्जेक्ट data एक पीएनजी छवि है, और यह बहुत बड़ा हो सकता है; जब भी इस विधि को बुलाया जाता है हम वस्तु के पूरे आकार को लीक कर रहे हैं। कल्पना करें कि हर बार एक टेबल सेल खींचा जाने पर इसे बुलाया जाता था: पूरे ऐप को तोड़ने में काफी समय नहीं लगेगा।

तो समस्याओं को ठीक करने के लिए हमें क्या करने की आवश्यकता है? , सही से पहले

-(UIImage *) downloadImageToFile { 

    // We own this object due to the alloc 
    NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

    // We don't own this object 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

    // We don't own this object 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 

    //[paths release] -- commented out, we don't own paths so can't release it 

    // We don't own this object 
    NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

    // We own this object due to the alloc 
    NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

    [url release]; //We're done with the url object so we can release it 

    [data writeToFile:path atomically:YES]; 

    [data release]; //We're done with the data object so we can release it 

    return [[UIImage alloc] initWithContentsOfFile:path]; 

    //We've released everything we owned so it's safe to leave the context 
} 

कुछ लोगों को एक ही बार में सब कुछ जारी करने के लिए पसंद करते हैं: यह बहुत सरल, हम बस वस्तुओं को रिहा करने की जरूरत है, जैसे ही हम उनकी आवश्यकता नहीं आमतौर पर सही पिछली बार के बाद वे इस्तेमाल कर रहे है संदर्भ विधि के अंत में बंद हो जाता है। उस स्थिति में [url release]; और [data release]; दोनों } ब्रेस बंद होने से ठीक पहले दिखाई देंगे। मुझे लगता है कि अगर मैं उन्हें कोड को साफ़ कर सकता हूं, तो कोड साफ़ हो जाता है, जब मैं इसे बाद में जाता हूं तो यह स्पष्ट करता है कि मैं ऑब्जेक्ट्स के साथ कहां से कर रहा हूं।

संक्षेप में प्रस्तुत करना: हम ही विधि में alloc, new, copy, या retain के साथ बनाई गई वस्तुओं कॉल इसलिए उन्हें रिलीज से पहले संदर्भ समाप्त होता है। हमारे पास कुछ और नहीं है और उन्हें कभी भी मुक्त नहीं करना चाहिए।


¹ वास्तव में चार शब्दों में जादुई कुछ भी नहीं है, वे सिर्फ एप्पल पर लोग सवाल में तरीकों बनाया द्वारा लगातार इस्तेमाल किया अनुस्मारक रहे हैं। यदि हम अपनी खुद की शुरुआत के लिए अपनी खुद की प्रारंभिकरण या प्रतिलिपि बनाने के तरीके बनाते हैं तो शब्दों को आवंटित करने, नए, प्रतिलिपि बनाने, या उचित तरीके से बनाए रखने की हमारी ज़िम्मेदारी है, और यदि हम उन्हें हमारे नामों में उपयोग नहीं करते हैं तो हम खुद को याद रखना होगा कि स्वामित्व पारित हो गया है या नहीं।

+0

बहुत बढ़िया स्पष्टीकरण !!! यह मेरे वॉलपेपर पर जा रहा है !!! – doNotCheckMyBlog

+0

सहमत, आश्चर्यजनक स्पष्ट स्पष्टीकरण। – Benjamin

+1

शायद इस साइट पर मैंने कभी भी सबसे अच्छा जवाब पढ़ा है। न केवल प्रश्न का उत्तर देता है बल्कि व्याख्या करने के लिए ऊपर और परे जाता है। – amcc

संबंधित मुद्दे