2011-03-29 15 views
126

संभव डुप्लिकेट:अग्रणी अंडरस्कोर के साथ आईओएस में संश्लेषित गुणों का नाम क्यों बदलें?

:
How does an underscore in front of a variable in a cocoa objective-c class work?

Xcode 4 में एक नई परियोजना बनाते समय, बॉयलरप्लेट कोड जब यह रूप में कार्यान्वयन फ़ाइल में ivars संश्लेषण करती अंडरस्कोर वर्ण कहते हैं

@synthesize window = _window; 

या:

@synthesize managedObjectContext = __managedObjectContext; 

क्या कोई मुझे बता सकता है कि यहां क्या किया जा रहा है? मैं एक पूर्ण घन नहीं हूं, लेकिन यह उद्देश्य का एक पहलू है- सी मुझे समझ में नहीं आता है।

भ्रम का एक और बिंदु; एप्लिकेशन प्रतिनिधि कार्यान्वयन में, जैसा कि ऊपर खिड़की इवर synthesizing, आवेदन didFinishLaunchingWithOptions में के बाद:

self.window.rootViewController = self.viewController 
[self.window makeKeyAndVisible]; 

लेकिन dealloc विधि यह _window है, या में _viewController

: विधि खिड़की और ViewController ivars स्वयं का उपयोग कर में भेजा जाता है

धन्यवाद

उत्तर

223

यह उद्देश्य-सी रनटाइम के पिछले संस्करण का एक आर्टिफैक्ट है।

मूल रूप से, @synthesize accessors तरीकों बनाने के लिए इस्तेमाल किया गया था, लेकिन क्रम अभी भी आवश्यक है कि उदाहरण चर स्पष्ट रूप से instantiated किया जा सकता था:

@interface Foo : Bar { 
    Baz *_qux; 
} 

@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux = _qux; 

- (void)dealloc { 
    [_qux release]; 
    [super dealloc]; 
} 

@end 

लोग अपने उदाहरण चर उपसर्ग उन्हें उनके गुणों से अलग हैं (भले ही ऐप्पल नहीं चाहता कि आप अंडरस्कोर का उपयोग करें, लेकिन यह एक अलग मामला है)। आप आवृत्ति चर पर इंगित करने के लिए संपत्ति को संश्लेषित करते हैं। लेकिन मुद्दा यह है कि, _qux एक आवृत्ति चर है और self.qux (या [self qux]) संदेश qux संदेश self पर भेजा गया संदेश है।

हम इंस्टेंस वैरिएबल का उपयोग सीधे -dealloc में करते हैं; बजाय एक्सेसर पद्धति का उपयोग करके इस प्रकार दिखाई देगा (हालांकि मैं इसकी सलाह नहीं देते, कारणों के लिए मैं शीघ्र ही तरीका बताएंगे):

- (void)dealloc { 
    self.qux = nil; // [self setQux:nil]; 
    [super dealloc]; 
} 

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

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

रनटाइम के बाद के संस्करणों ने एक्सेसर विधियों के अतिरिक्त इंस्टेंस चर को संश्लेषित करने की क्षमता को जोड़ा। क्रम के इन संस्करणों के साथ, ऊपर कोड उदाहरण चर को छोड़ते हुए लिखा जा सकता है:

@interface Foo : Bar 
@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux = _qux; 

- (void)dealloc { 
    [_qux release]; 
    [super dealloc]; 
} 

@end 

यह वास्तव में Foo पर एक उदाहरण चर _qux कहा जाता है, जो मनुष्य और सेटर संदेशों -qux और -setQux: द्वारा पहुँचा है संश्लेषण करती।

मैं इसके खिलाफ अनुशंसा करता हूं: यह थोड़ा गन्दा है, लेकिन अंडरस्कोर का उपयोग करने का एक अच्छा कारण है; अर्थात्, गलती से सीधे ivar पहुंच के खिलाफ सुरक्षा के लिए। तब

@interface Foo : Bar 
@property (retain) Baz *qux; 
@end 

@implementation Foo 
@synthesize qux; 

- (void)dealloc { 
    [qux release]; 
    [super dealloc]; 
} 

@end 

, जब आप सीधे उदाहरण चर का उपयोग करना चाहते हैं, बस: आपको लगता है कि आप यदि आप एक कच्चे उदाहरण चर या एक्सेसर विधि का उपयोग कर रहे हैं कि क्या याद करने के लिए अपने आप को भरोसा कर सकते हैं, तो बस इसे इस तरह के बजाय करना qux (जो संकेतक से किसी सदस्य तक पहुंचने के लिए सी वाक्यविन्यास में self->qux का अनुवाद करता है)। जब आप एक्सेसर्स विधियों का उपयोग करना चाहते हैं (जो पर्यवेक्षकों को सूचित करेंगे, और अन्य रोचक चीजें करते हैं, और स्मृति प्रबंधन के संबंध में चीजों को सुरक्षित और आसान बनाते हैं), self.qux ([self qux]) और self.qux = blah; ([self setQux:blah]) का उपयोग करें।

दुख की बात यह है कि ऐप्पल का नमूना कोड और टेम्पलेट कोड बेकार है। उचित उद्देश्य-सी शैली के लिए इसे कभी भी गाइड के रूप में उपयोग न करें, और निश्चित रूप से इसे उचित सॉफ़्टवेयर आर्किटेक्चर के लिए मार्गदर्शिका के रूप में कभी भी उपयोग न करें। :)

+59

'@synthesize quz = _quz लिए एक बहुत अच्छा कारण नहीं है; '; जब आप 'self.quz' और इसके विपरीत' होते हैं तो यह गलती से 'quz' लिखता है। संकलक मुद्दा अपेक्षाकृत कम रहता था, लेकिन असली। यदि आपको बोर्क किए गए उदाहरण मिलते हैं, तो कृपया बग दर्ज करें। – bbum

+1

@bbum अच्छा बिंदु फिर अंडरस्कोर-नामकरण।मैं आमतौर पर सही चीज़ टाइप करने के लिए खुद पर भरोसा करता हूं (या कम से कम इसे ठीक करने के लिए इसे ठीक करें), लेकिन यह निश्चित रूप से कुछ है जो आपके कोडिंग-शैली को क्राफ्ट करते समय सोचता है (मैं सौंदर्यशास्त्र के पक्ष में गलती करता हूं, लेकिन यह पूरी तरह से मान्य है दुर्घटनाओं के खिलाफ सुरक्षा की ओर दुबला)। –

+0

यदि आप हाथ से सभी बॉयलरप्लेट लिखना नहीं चाहते हैं तो आप https://github.com/holtwick/xobjc (ओपन सोर्स प्रोजेक्ट के शर्मनाक स्व प्रोमो;) का उपयोग करना चाहेंगे) यह अग्रणी और पिछला अंडरस्कोर का समर्थन करता है । पिछली बार Google स्टाइलगाइड द्वारा ओबीजेसी के लिए अनुशंसा की जाती है। यह भी उपयोगी है, क्योंकि ऐप्पल अपने कोड में अग्रणी अंडरस्कोर का भी उपयोग करता है और इसलिए आपको दुर्लभ मामलों में निजी एपीआई के साथ परेशानी हो सकती है। – Holtwick

6
आवेदन didFinishLaunchingWithOptions में

: विधि खिड़की और ViewController ivars स्वयं

का उपयोग कर में भेजा जाता है

नहीं, वे नहीं हैं। वे गुणwindow और viewController के संदर्भ हैं। यह अंडरस्कोर का मुद्दा है, जब संपत्ति का उपयोग किया जा रहा है (कोई अंडरस्कोर नहीं) और जब इवर को सीधे एक्सेस किया जा रहा है (अंडरस्कोर के साथ)।

2

हां, यह ऑब्जेक्ट के संदर्भ को अलग करने के लिए है। यही है, अगर वस्तु का संदर्भ सीधे अंडरस्कोर के साथ उपयोग किया जाता है, अन्यथा ऑब्जेक्ट को संदर्भित करने के लिए स्वयं का उपयोग करें।

13

यहां एक और कारण है।

@implementation ScaryBugData 
@synthesize title; 
@synthesize rating; 
- (id)initWithTitle:(NSString *)title rating:(float)rating { 
    if (self = [super init]) { 
     self.title = title; // Warning. Local declaration hides instance variable 
     self.rating = rating; // Warning. Local declaration hides instance variable 
    } 
    return self; 
} 
@end 

आप उदाहरण चर को रेखांकित द्वारा चेतावनी से बचने के: उदाहरण चर को रेखांकित बिना आप अक्सर मापदंडों self.title = title और self.rating = rating साथ चेतावनी प्राप्त

@implementation ScaryBugData 
    @synthesize title = _title; 
    @synthesize rating = _rating; 
    - (id)initWithTitle:(NSString *)title rating:(float)rating { 
     if (self = [super init]) { 
      self.title = title; // No warning 
      self.rating = rating; // No warning 
     } 
     return self; 
    } 
    @end 
+0

[रे वेंडरलिच के महान डरावनी बग ऐप ट्यूटोरियल] से नमूना कोड प्यार करने के लिए मिला [http://www.raywenderlich.com/1797/ios-tutorial-how-to-create-a-simple-iphone-app-part-1) :) – nicksuch

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