2011-04-23 11 views
5

आरंभ गलत वर्ग के साथ भ्रमित किया जा रहा मैंने सोचा कि मैं जहाँ तक बुनियादी ऑब्जेक्टिव-सी समझ में आ के रूप में alloc और init ... तरीकों जाते हैं, लेकिन जाहिरा तौर पर मैं नहीं। मैंने नीचे दी गई न्यूनतम उदाहरण में सामने आने वाली समस्या को उबाल दिया है। (उदाहरण के लिए मैंने सभी स्रोतों को एक फ़ाइल में रखा है, लेकिन समस्या तब होती है जब स्रोत को एकाधिक स्रोत और हेडर फाइलों में विभाजित किया जाता है क्योंकि यह सामान्य रूप से होगा)।ऑब्जेक्टिव-सी वर्ग alloc द्वारा दिया जब

यहाँ कोड करता है का सारांश है और क्या होता है जब मैं इसे चलाते हैं।

मुझे लगता है कि सिवाय प्रकार int नाव के साथ दूसरे के साथ कि एक सौदों लगभग समान हैं दो वर्गों MyInteger और MyFloat, परिभाषित करते हैं। दोनों में प्रारंभिक विधियों को initWithValue कहा जाता है: हालांकि विभिन्न प्रकार के तर्क के साथ। यह नियंत्रित करने के लिए एक परिभाषा है कि MyInteger क्लास को परिभाषित किया गया है या नहीं, इसका कारण यह है कि यह प्रोग्राम के विभिन्न व्यवहार की ओर जाता है, भले ही कक्षा का कभी भी उपयोग नहीं किया जाता है।

मुख्य() केवल MyFloat उपयोग करता है। पहली दो पंक्तियां यह करती हैं: MyFloat का एक उदाहरण आवंटित करती है, और इसे 50.0 के मान के साथ प्रारंभ करती है। फिर यह मूल्य प्रिंट करता है। इस पर निर्भर करता है कि MyInteger परिभाषित किया गया है या नहीं, मुझे दो अलग-अलग आउटपुट मिलते हैं।

बिना MyInteger परिभाषित, बस के रूप में मैं उम्मीद करेंगे:


[4138:903] float parameter value:50.000000 
[4138:903] Value:50.000000 
(next two output lines omitted) 

MyInteger साथ

परिभाषित, मेरे आश्चर्य करने के लिए बहुत:


[4192:903] float parameter value:0.000000 
[4192:903] Value:0.000000 
(next two output lines omitted) 

मुझे ऐसा लगता है कि संकलक initWithValue करने के लिए कॉल व्यवहार करता है: जैसे कि यह अंतर्गत आता है MyInteger कक्षा में। मुख्य() की अगली दो पंक्तियां MyFloat * टाइप करने के लिए [MyFloat alloc] कास्टिंग करके इसका परीक्षण करें। और वह उत्पादन की उम्मीद के रूप में उत्पन्न करता है, तब भी जब MyInteger परिभाषित किया गया है:


[4296:903] float parameter value:0.000000 
[4296:903] Value:0.000000 
[4296:903] float parameter value:50.000000 
[4296:903] Value with cast:50.000000 

कृपया व्याख्या क्या हो रहा है! मैंने 24 घंटों से अधिक समय तक इसके साथ संघर्ष किया है, यहां तक ​​कि दरवाजे खोलने के बिंदु तक कुछ गर्मी छोड़ने के लिए भी मेरा कंप्यूटर ठंडा हो सकता है :-) धन्यवाद!

एक और विषमता है कि अगर मैं MyFloat की परिभाषा नीचे MyInteger की परिभाषा नीचे ले जाते हैं, तो सब कुछ "अच्छा" है - काम करता है के रूप में मैं उम्मीद होती है। इतिहास ने मुझे संदेह करने के लिए अक्सर गलत साबित कर दिया है कि संकलक को दोष देना है। किसी भी मामले में, यहां संकलक और परियोजना की जानकारी है: एक्सकोड 4.0.2 का उपयोग करना। सभी 3 कंपाइलर विकल्पों (जीसीसी 4.2, एलएलवीएम जीसीसी 4.2, और एलएलवीएम कंपाइलर 2.0) के साथ प्रयास किया। इस उदाहरण के लिए एक्सकोड प्रोजेक्ट फाउंडेशन के आधार पर मैक ओएस एक्स कमांड लाइन टूल के लिए मानक कॉन्फ़िगरेशन का उपयोग करके सेटअप किया गया था।


#define DO_DEFINE_MYINTEGER 1 

//------------- define MyInteger -------------- 
#if DO_DEFINE_MYINTEGER 
@interface MyInteger : NSObject { 
    int _value; 
} 
-(id)initWithValue:(int)value; 
@end 

@implementation MyInteger 
-(id)initWithValue:(int)value { 
    self= [super init]; 
    if (self) { 
     _value= value; 
    } 
    return self; 
} 
@end 
#endif 


//------------- define MyFloat -------------- 
@interface MyFloat : NSObject { 
    float _value; 
} 
-(id)initWithValue:(float)value; 
-(float)theValue; 
@end 

@implementation MyFloat 
-(id)initWithValue:(float)value { 
    self= [super init]; 
    if (self) { 
     NSLog(@"float parameter value:%f",value); 
     _value= value; 
    } 
    return self; 
} 
-(float)theValue { 
    return _value; 
} 
@end 

//--------------- main ------------------------ 
int main (int argc, const char * argv[]) 
{ 
    MyFloat *mf1= [[MyFloat alloc] initWithValue:50.0f]; 
    NSLog(@"Value:%f",[mf1 theValue]); 

    MyFloat *mf2= [((MyFloat*)[MyFloat alloc]) initWithValue:50.0f]; 
    NSLog(@"Value with cast:%f",[mf2 theValue]); 

    return 0; 
} 
+0

मैंने शेरम पेंडले से सही जवाब के रूप में जवाब चिह्नित किया है, क्योंकि यह ऐसा क्यों होता है, यह मैकेनिक्स बताता है। एनएसआरस्पॉन्डर का उत्तर भी एक अच्छा है, क्योंकि यह एक हस्ताक्षर नामकरण अभ्यास सिखाता है जो समस्या से बचने में मदद करेगा। धन्यवाद दोनों! – rene

उत्तर

5

+allocid लौटने के रूप में नमूने के है, और जब संकलक कई -initWithValue: तरीकों में से एक विकल्प के साथ सामना करना पड़ा है, यह पहले एक यह पाता है फोन करने के लिए कोड उत्पन्न करता है। जब MyInteger परिभाषित किया गया है, इसका मतलब है कि संकलक 50.0 रूपांतरित करने के लिए कोड उत्पन्न करेगा और इसे पूर्णांक तर्क के रूप में पास करेगा।ध्यान दें कि पूर्णांक और फ्लोट तर्क अलग-अलग पारित होते हैं, पूर्व में स्टैक और बाद वाले फ़्लोटिंग-पॉइंट रजिस्टर में।

रन टाइम पर, क्योंकि संदेश प्रेषण गतिशील रूप से संभाला जाता है, सही विधि कहा जाता है - लेकिन वह विधि मानती है कि value तर्क फ़्लोटिंग-पॉइंट रजिस्टर में पारित किया गया था। लेकिन यह वह जगह नहीं है जहां कॉलिंग कोड रखा गया है, इसलिए जब वह रजिस्टर पढ़ता है तो बुलाया गया विधि गलत परिणाम प्राप्त करता है।

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

संपादित करें: यह सब कहा जा रहा है, एनएसआरस्पॉन्डर का जवाब कुछ बहुत अच्छे अंक भी बनाता है। उद्देश्य-सी में, एक ही नाम साझा करने वाली विधियों को घोषित करना बहुत बुरा विचार है, लेकिन अलग-अलग हस्ताक्षर (यानी & रिटर्न प्रकार) हैं, और -initWithValue: नामक एक विधि का तात्पर्य है कि इसका तर्क एक NSValue ऑब्जेक्ट है।

+0

धन्यवाद! ऐसा लगता है कि क्या हो रहा है। ऐप्पल के ऑलोक दस्तावेज का कहना है, "नए इंस्टेंस का आईएसए इंस्टेंस वैरिएबल एक डेटा स्ट्रक्चर के लिए शुरू किया गया है जो कक्षा का वर्णन करता है" जिसे मैंने लिया था कि संकलक जानता है कि कौन सी कक्षा को तत्काल बनाया गया है, और दाईं ओर कोड उत्पन्न करने में सक्षम होगा एक। लेकिन अब मुझे एहसास है कि यह समय संकलित करने में मदद नहीं करता है। एक्सकोड का स्वत: पूर्णता केवल सही विधि (फ्लोट प्रकार के साथ) का सुझाव देने के लिए पर्याप्त स्मार्ट है - जो मेरे भ्रम में जोड़ा गया है। – rene

+0

आपके द्वारा उद्धृत किए गए दस्तावेज़ों का वर्णन करता है कि रन टाइम पर क्या होता है - संकलन समय पर, सभी कंपाइलर जानता है कि यह एक आईडी में '-initWithValue:' संदेश भेज रहा है, जो कि किसी भी वर्ग का हो सकता है। –

4

यह एक बुरा विचार है, जहां विधि के नाम समान हैं, लेकिन प्रकार के समान नहीं हैं दो विधि हस्ताक्षर करने के लिए, वे एक ही फाइल में संकलित किया जा रहा हो, खासकर अगर। संकलक यहां भ्रमित हो रहा है कि संदेश अभिव्यक्ति एक int या एक फ्लोट पास होनी चाहिए।

आप NSNumber को देखें, तो तुम वहाँ उदाहरण के लिए अलग -initWithFloat:, -initWithDouble: और -initWithInt: तरीके हैं कि, देखेंगे।

इसके अलावा, यदि आपके नाम में "मूल्य" शब्द के साथ कोई विधि है, तो अधिकांश कोको डेवलपर्स मान लेंगे कि यह NSValue पैरामीटर की अपेक्षा करता है।

+1

धन्यवाद। मैंने यह स्पष्ट करने के लिए प्रश्न संपादित किया कि समस्या तब भी उत्पन्न होती है जब स्रोत अलग .m और .h फ़ाइलों में विभाजित होता है। बेशक, MyInteger.h और MyFloat.h दोनों को फ़ाइल में शामिल किया जाएगा जो कॉल करता है, इसलिए जब आप इंगित करते हैं तो कंपाइलर अभी भी भ्रमित हो सकता है। मैं मूल्य सलाह को दर्शाने के लिए आपकी सलाह लेता हूं और हस्ताक्षर बदलता हूं। फिर भी, मुझे यह परेशानी होती है कि मैं व्यवहार की भविष्यवाणी नहीं कर सकता और यह व्यवहार MyInteger.h और MyFloat.h आयात के आदेश के आधार पर बदलता है। तो क्या वास्तव में यह माना जाता है कि यह कैसे होना चाहिए? – rene

+0

लेकिन वे अलग-अलग वर्गों में हैं, क्या आपको यह पता होना चाहिए कि आप किस वर्ग को संदेश भेजते हैं? –

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