उद्देश्य-सी में पढ़ें गुण?


93

मैंने अपने इंटरफ़ेस में एक समान संपत्ति घोषित की है:

 @property (readonly, nonatomic, copy) NSString* eventDomain;

हो सकता है कि मैं गुणों को गलत समझ रहा हूं, लेकिन मुझे लगा कि जब आप इसे घोषित करते हैं readonly, तो आप कार्यान्वयन ( .m) फ़ाइल के अंदर जेनरेट किए गए सेटर का उपयोग कर सकते हैं , लेकिन बाहरी निकाय मान को बदल नहीं सकते। यह SO प्रश्न कहता है कि क्या होना चाहिए। यही वह व्यवहार है जिसके बाद मैं हूं। हालांकि, जब eventDomainमेरे इनिट विधि के अंदर स्थापित करने के लिए मानक सेटर या डॉट सिंटैक्स का उपयोग करने का प्रयास किया जाता है , तो यह मुझे एक unrecognized selector sent to instance.त्रुटि देता है । बेशक मैं @synthesizeसंपत्ति में हूँ । इसे इस तरह इस्तेमाल करने की कोशिश की जा रही है:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

तो क्या मैं readonlyएक संपत्ति पर घोषणा को गलत समझ रहा हूं ? या कुछ और चल रहा है?

जवाबों:


116

आपको संकलक को यह बताने की आवश्यकता है कि आप एक सेटर भी चाहते हैं। एक सामान्य तरीका इसे .m फ़ाइल में एक क्लास एक्सटेंशन में रखना है :

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end

22
यह एक श्रेणी नहीं है। यह एक क्लास एक्सटेंशन है (जैसा कि ईको ने कहा है)। वे विशिष्ट रूप से भिन्न हैं, हालांकि समान उद्देश्यों के लिए उपयोग किया जाता है। उदाहरण के लिए, आप एक वास्तविक श्रेणी में ऊपर नहीं कर सकते।
बीबी 18

2
मैंने देखा कि एक वर्ग जो उस वर्ग से विरासत में मिला है जिसमें वर्ग विस्तार गुण हैं, उन्हें नहीं देखेगा। यानी इनहेरिटेंस केवल बाहरी इंटरफ़ेस को देखते हैं। पुष्टि करें?
योगेव शेली

5
यह काफी निष्पक्ष नहीं है। यह अलग हो सकता है, लेकिन इसे "बेनामी श्रेणी" के रूप में जाना जाता है
मैक्सगैब्रियल

3
क्या यह सार्वजनिक इंटरफ़ेस में ऑप की प्रारंभिक घोषणा के अतिरिक्त है? मतलब, क्या इस वर्ग विस्तार की घोषणा प्रारंभिक घोषणा को ओवरराइड करती है .h? अन्यथा मैं नहीं देखता कि यह एक सार्वजनिक सेटर को कैसे उजागर करेगा। धन्यवाद
Madbreaks

4
@Madbreaks यह अतिरिक्त है, लेकिन यह .m फ़ाइल में है। इसलिए संकलक इसे उस वर्ग के लिए रीडराइट बनाना जानता है, लेकिन बाहरी उपयोग अभी भी उम्मीद के मुताबिक केवल पढ़ने के लिए सीमित है।
ईको

38

इको और अन्य लोगों ने सही उत्तर दिए।

यहां एक सरल तरीका है: सीधे निजी सदस्य चर पर पहुंचें।

उदाहरण

शीर्ष लेख में .h फ़ाइल:

@property (strong, nonatomic, readonly) NSString* foo;

कार्यान्वयन में .m फ़ाइल:

// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.

बस, इतना ही चाहिए। कोई उपद्रव कोई अव्यवस्था नहीं।

विवरण

Xcode 4.4 और LLVM कंपाइलर 4.0 ( Xcode 4.4 में नए फीचर्स ) के रूप में, आपको अन्य उत्तरों में चर्चा किए गए कामों से खिलवाड़ करने की आवश्यकता नहीं है:

  • synthesizeकीवर्ड
  • एक चर की घोषणा
  • कार्यान्वयन .m फ़ाइल में गुण को फिर से घोषित करना।

एक संपत्ति की घोषणा करने के बाद foo, आप मान सकते हैं कि Xcode ने अंडरस्कोर के एक उपसर्ग के साथ नामित एक निजी सदस्य चर जोड़ा है _foo:।

यदि संपत्ति घोषित की गई थी readwrite, तो Xcode नाम की एक गेट्टर विधि fooऔर नाम सेटर सेट उत्पन्न करता है setFoo। जब आप डॉट नोटेशन (मेरा Object.myMethod) का उपयोग करते हैं तो इन तरीकों को स्पष्ट रूप से कहा जाता है। यदि संपत्ति घोषित की गई थी readonly, तो कोई सेटर उत्पन्न नहीं होता है। इसका मतलब है कि बैकिंग वेरिएबल, अंडरस्कोर के साथ नामित किया गया है, जो स्वयं पढ़ा नहीं जाता है। readonlyबस इसका मतलब यह है कि कोई सेटर विधि को संश्लेषित नहीं किया गया था, और इसलिए एक संकलक त्रुटि के साथ एक मान सेट करने के लिए डॉट नोटेशन का उपयोग करना। डॉट नोटेशन विफल हो जाता है क्योंकि कंपाइलर आपको एक विधि (सेटर) को कॉल करने से रोकता है जो मौजूद नहीं है।

इसके आस-पास सबसे सरल तरीका सीधे सदस्य चर का उपयोग करना है, जिसका नाम अंडरस्कोर है। आप अंडरस्कोर-नामित चर घोषित किए बिना भी ऐसा कर सकते हैं! Xcode उस घोषणा को निर्माण / संकलन प्रक्रिया के हिस्से के रूप में सम्मिलित कर रहा है, इसलिए आपके संकलित कोड में वास्तव में परिवर्तनशील घोषणा होगी। लेकिन आप अपने मूल स्रोत कोड फ़ाइल में उस घोषणा को कभी नहीं देखते हैं। जादू नहीं, सिर्फ वाक्यगत चीनी

उपयोग self->वस्तु / उदाहरण के एक सदस्य चर का उपयोग करने का एक तरीका है। आप इसे छोड़ सकते हैं, और बस var नाम का उपयोग कर सकते हैं। लेकिन मैं स्वयं + तीर का उपयोग करना पसंद करता हूं क्योंकि यह मेरे कोड को स्व-दस्तावेजीकरण बनाता है। जब आप देखते हैं कि self->_fooआप अस्पष्टता के बिना जानते हैं जो _fooइस उदाहरण पर एक सदस्य चर है।


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


1
शायद आपको यह जोड़ना चाहिए कि किसी सदस्य को सीधे अपने वर्ग से (बिना वर्ग के) चर का उपयोग नहीं करना चाहिए! ऐसा करना OOP (सूचना छिपाना) का उल्लंघन है।
HAS

2
@ सही है, यहां का परिदृश्य इस वर्ग के बाहर से नहीं देखे जाने वाले सदस्य चर को निजी रूप से सेट कर रहा है। यह मूल पोस्टर के प्रश्न का संपूर्ण बिंदु है: एक संपत्ति की घोषणा करना readonlyताकि कोई अन्य वर्ग इसे निर्धारित न कर सके।
तुलसी Bourque

मैं इस पोस्ट को देख रहा था कि कैसे एक आसानी से संपत्ति बनाने के लिए। यह मेरे लिए काम नहीं करता है। यह मुझे init में मान निर्दिष्ट करने की अनुमति देता है, लेकिन मैं असाइनमेंट के साथ बाद में _variable भी बदल सकता हूं। यह संकलक त्रुटि या चेतावनी नहीं बढ़ाता है।
netskink

@BasilBourque उस "दृढ़ता" प्रश्न पर सहायक संपादन के लिए आपका बहुत-बहुत धन्यवाद!
घोस्टकट Ghost

36

एक और तरीका है कि मैंने आसानी से गुणों के साथ काम किया है, बैकिंग स्टोर को निर्दिष्ट करने के लिए @synthesize का उपयोग करना है। उदाहरण के लिए

@interface MyClass

@property (readonly) int whatever;

@end

फिर कार्यान्वयन में

@implementation MyClass

@synthesize whatever = m_whatever;

@end

आपकी विधियाँ तब सेट हो सकती हैं m_whatever, क्योंकि यह एक सदस्य चर है।


एक और दिलचस्प बात जो मैंने पिछले कुछ दिनों में महसूस की है, वह यह है कि आप आसानी से ऐसे गुण बना सकते हैं जो उपवर्गों द्वारा इस तरह के हैं:

(हेडर फ़ाइल में)

@interface MyClass
{
    @protected
    int m_propertyBackingStore;
}

@property (readonly) int myProperty;

@end

फिर, कार्यान्वयन में

@synthesize myProperty = m_propertyBackingStore;

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

हालांकि डेटा छिपाने और इनकैप्सुलेशन के संदर्भ में थोड़ा अफसोस है।


1
आपके द्वारा वर्णित पहला तरीका स्वीकृत उत्तर की तुलना में आसान है। बस "@synthesize foo1, foo2, foo3;" में .m, और सब अच्छा है।
सूदो

20

IOS डॉक्स में मौजूदा कक्षाओं को अनुकूलित करना देखें ।

आसानी से इंगित करता है कि संपत्ति केवल-पढ़ने के लिए है। यदि आप आसानी से निर्दिष्ट करते हैं, तो @im कार्यान्वयन में केवल एक गेट्टर विधि की आवश्यकता होती है। यदि आप कार्यान्वयन ब्लॉक में @synthesize का उपयोग करते हैं, तो केवल गेट्टर विधि संश्लेषित होती है। इसके अलावा, यदि आप डॉट सिंटैक्स का उपयोग करके एक मान असाइन करने का प्रयास करते हैं, तो आपको एक कंपाइलर त्रुटि मिलती है।

Readonly गुणों में केवल एक गेटर विधि है। आप अभी भी बैकिंग ivar को सीधे संपत्ति के वर्ग के भीतर या मुख्य मूल्य कोडिंग का उपयोग करके सेट कर सकते हैं।


9

आप दूसरे प्रश्न को गलत समझ रहे हैं। इस सवाल में एक वर्ग विस्तार है, इस प्रकार घोषित किया गया है:

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end

वह वह है जो केवल कक्षा के कार्यान्वयन के भीतर दिखाई देने वाले सेटर को उत्पन्न करता है। तो जैसा कि ईको कहता है, आपको एक वर्ग विस्तार की घोषणा करने की जरूरत है और केवल वर्ग के भीतर एक सेटर उत्पन्न करने के लिए संकलक को बताने के लिए संपत्ति की घोषणा को ओवरराइड करना होगा।


5

सबसे छोटा समाधान है:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end

2

यदि किसी संपत्ति को आसानी से परिभाषित किया जाता है, तो इसका मतलब है कि प्रभावी रूप से एक सेटर नहीं होगा जिसे या तो आंतरिक रूप से कक्षा में उपयोग किया जा सकता है या अन्य वर्गों से बाहरी रूप से। (अर्थात: यदि आपके पास केवल "गटर" होगा तो यह समझ में आएगा। "

इसकी ध्वनियों से, आप एक सामान्य पढ़ने / लिखने की संपत्ति चाहते हैं जिसे निजी के रूप में चिह्नित किया गया है, जिसे आप अपनी इंटरफ़ेस फ़ाइल में निजी के रूप में वर्ग चर सेट करके प्राप्त कर सकते हैं:

@private
    NSString* eventDomain;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.