उद्देश्य-सी में एक वर्ग के लिए निजी तरीकों को परिभाषित करने का सबसे अच्छा तरीका


355

मैंने अभी-अभी ऑब्जेक्टिव-सी प्रोग्रामिंग शुरू की है और जावा में एक बैकग्राउंड है, आश्चर्य है कि ऑब्जेक्टिव-सी प्रोग्राम लिखने वाले लोग निजी तरीकों से कैसे निपटते हैं।

मैं समझता हूं कि कई सम्मेलन और आदतें हो सकती हैं और इस सवाल के बारे में सोचना चाहिए कि उद्देश्य-सी में निजी तरीकों से निपटने के लिए सबसे अच्छी तकनीक का उपयोग करने वाले लोग हैं।

कृपया इसे पोस्ट करते समय अपने दृष्टिकोण के लिए एक तर्क शामिल करें। क्यों अच्छा है? इसमें कौन सी कमियां हैं (जो आप जानते हैं) और आप उनसे कैसे निपटते हैं?


मेरे निष्कर्षों के लिए अब तक।

समूह निजी विधियों के लिए MyClass.m फ़ाइल में परिभाषित [उदाहरण MyClass (निजी)] श्रेणियों का उपयोग करना संभव है ।

इस दृष्टिकोण में 2 मुद्दे हैं:

  1. Xcode (और संकलक?) यह जाँच नहीं करता है कि क्या आप इसी श्रेणी में सभी तरीकों को परिभाषित करते हैं
  2. आपको MyClass.m फाइल की शुरुआत में अपनी निजी श्रेणी की घोषणा करते हुए @interface डालना होगा, अन्यथा Xcode एक संदेश के साथ शिकायत करता है जैसे "आत्म" संदेश का जवाब नहीं दे सकता है "PrivateFoo"।

पहले अंक को खाली श्रेणी [जैसे MyClass ()] के साथ काम किया जा सकता है ।
दूसरा मुझे बहुत परेशान करता है। मैं फ़ाइल के अंत में लागू निजी तरीकों (और परिभाषित) को देखना चाहता हूं; मुझे नहीं पता कि यह संभव है।


1
दोस्तों को यह सवाल रोचक लग सकता है: stackoverflow.com/questions/2158660/…
bbum

जवाबों:


436

जैसा कि अन्य ने पहले ही कहा है, उद्देश्य-सी में एक निजी पद्धति के रूप में ऐसा नहीं है। हालाँकि, ऑब्जेक्टिव-सी 2.0 (जिसका अर्थ है मैक ओएस एक्स लेपर्ड, आईफोन ओएस 2.0, और बाद में) शुरू करना आप एक खाली नाम (यानी @interface MyClass ()) के साथ एक श्रेणी बना सकते हैं जिसे क्लास एक्सटेंशन कहा जाता है । क्लास एक्सटेंशन के बारे में जो बात अनोखी है, वह यह है कि जिस तरीके @implementation MyClassसे पब्लिक इंप्लायमेंट्स को लागू किया जाना चाहिए, वही लागू होना चाहिए । इसलिए मैं अपनी कक्षाओं को इस तरह से तैयार करता हूं:

.H फ़ाइल में:

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

और .m फ़ाइल में:

@interface MyClass()

- (void)myPrivateMethod;

@end

@implementation MyClass

- (void)myPublicMethod {
    // Implementation goes here
}

- (void)myPrivateMethod {
    // Implementation goes here
}

@end

मुझे लगता है कि इस दृष्टिकोण का सबसे बड़ा लाभ यह है कि यह आपको अपनी कार्यप्रणाली को कार्यक्षमता द्वारा समूहित करने की अनुमति देता है, न कि सार्वजनिक (निजी) भेद द्वारा।


8
और यह "MYClass '-myPStreetMethod-" का जवाब नहीं दे सकता है, अपवाद / त्रुटि नहीं।
34zgür

2
यह वास्तव में एप्पल के बॉयलरप्लेट कोड में दिखाई देने लगा है। ++
क्रिस ट्रेही

75
LLVM 4 संकलक और उसके बाद, आपको ऐसा करने की आवश्यकता नहीं है। आप उन्हें केवल एक वर्ग विस्तार में डालने की आवश्यकता के बिना अपने कार्यान्वयन के भीतर उन्हें परिभाषित कर सकते हैं।
अबिज़र्न

1
अगर आपको चेतावनियाँ मिलती हैं @Comptrol का उल्लेख है, तो इसका कारण यह है कि आपने किसी अन्य विधि के बजाय नीचे एक विधि को परिभाषित किया है जो इसे कहता है (देखें एंडी का उत्तर) - और आप इन चेतावनियों को अपने जोखिम पर ध्यान नहीं देते हैं। मैंने यह गलती की और कंपाइलर ठीक से तब तक if (bSizeDifference && [self isSizeDifferenceSignificant:fWidthCombined])...घुलमिल गया जब तक कि मैंने इस तरह से कॉल नहीं किया: तब fWidthCombined हमेशा 0. के माध्यम से आ रहा था
Wienke

6
@Wienke यह आदेश के बारे में चिंता करने के लिए आवश्यक नहीं है। एलएलवीएम के हालिया संस्करणों को विधि मिल जाएगी भले ही यह नीचे दिखाई दे जहां इसे कहा जाता है।
स्टीव वाडिकॉर

52

ऑब्जेक्टिव-सी में वास्तव में "निजी पद्धति" नहीं है, अगर रनटाइम काम कर सकता है तो इसका उपयोग करने के लिए कौन सा कार्यान्वयन करेगा। लेकिन यह कहना नहीं है कि ऐसे तरीके नहीं हैं जो दस्तावेज इंटरफेस का हिस्सा नहीं हैं। उन तरीकों के लिए मुझे लगता है कि एक श्रेणी ठीक है। @interfaceअपने बिंदु 2 की तरह .m फ़ाइल के शीर्ष पर रखने के बजाय , मैं इसे अपनी .h फ़ाइल में डालूंगा। एक ऐसा सम्मेलन जिसका मैं अनुसरण करता हूं (और कहीं और देखा है, मुझे लगता है कि यह एक Apple सम्मेलन है क्योंकि Xcode अब इसके लिए स्वत: समर्थन देता है) इस तरह की फ़ाइल का नाम इसके वर्ग और श्रेणी के साथ + अलग करने के बाद है, इसलिए इसमें @interface GLObject (PrivateMethods)पाया जा सकता है GLObject+PrivateMethods.h। शीर्ष लेख फ़ाइल प्रदान करने का कारण यह है कि आप इसे अपनी इकाई परीक्षण कक्षाओं :-) में आयात कर सकते हैं।

वैसे, जहाँ तक .m फ़ाइल के अंत में विधियों को लागू करने / परिभाषित करने का संबंध है, आप ऐसा कर सकते हैं कि .m फ़ाइल के निचले भाग में श्रेणी को लागू करके श्रेणी के साथ:

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

या एक वर्ग विस्तार के साथ (जिस चीज को आप "खाली श्रेणी" कहते हैं), बस उन तरीकों को अंतिम रूप से परिभाषित करें। उद्देश्य-सी विधियों को परिभाषित किया जा सकता है और कार्यान्वयन में किसी भी क्रम में उपयोग किया जा सकता है, इसलिए फ़ाइल के अंत में आपको "निजी" विधियों को रोकने के लिए कुछ भी नहीं है।

यहां तक ​​कि क्लास एक्सटेंशन के साथ मैं अक्सर एक अलग हेडर ( GLObject+Extension.h) बनाऊंगा ताकि आवश्यकता पड़ने पर उन तरीकों का उपयोग कर सकूं, "मित्र" या "संरक्षित" दृश्यता की नकल करें।

चूंकि यह उत्तर मूल रूप से लिखा गया था, इसलिए क्लैग कंपाइलर ने ऑब्जेक्टिव-सी विधियों के लिए दो पास करना शुरू कर दिया है। इसका मतलब है कि आप अपने "निजी" तरीकों को पूरी तरह से घोषित करने से बच सकते हैं, और चाहे वे कॉलिंग साइट के ऊपर या नीचे हों, वे कंपाइलर द्वारा मिल जाएंगे।


37

जबकि मैं कोई उद्देश्य-सी विशेषज्ञ नहीं हूं, मैं व्यक्तिगत रूप से अपनी कक्षा के कार्यान्वयन में विधि को परिभाषित करता हूं। दी गई, इसे किसी भी तरीके से बुलाने से पहले (ऊपर) परिभाषित किया जाना चाहिए, लेकिन यह निश्चित रूप से काम करने में कम से कम राशि लेता है।


4
इस समाधान का यह फायदा है कि यह एक कंपाइलर चेतावनी से बचने के लिए केवल शानदार प्रोग्राम संरचना को जोड़ने से बचता है।
जन हेटिच

1
मैं ऐसा करना चाहता हूं, लेकिन मैं एक उद्देश्य-सी विशेषज्ञ भी नहीं हूं। विशेषज्ञों के लिए, क्या इस तरह से ऐसा न करने का कोई कारण है (एक तरफ विधि आदेश जारी करने से)?
एरिक स्मिथ

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

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

@Justin
lagweezle

19

@implementationब्लॉक में अपने निजी तरीकों को परिभाषित करना अधिकांश उद्देश्यों के लिए आदर्श है। @implementationघोषणा आदेश की परवाह किए बिना, क्लैंग इन के भीतर देखेंगे । उन्हें कक्षा निरंतरता (उर्फ क्लास एक्सटेंशन) या नामित श्रेणी में घोषित करने की आवश्यकता नहीं है।

कुछ मामलों में, आपको कक्षा की निरंतरता में विधि की घोषणा करने की आवश्यकता होगी (जैसे यदि वर्ग निरंतरता और @implementation) के बीच चयनकर्ता का उपयोग करना ।

static विशेष रूप से संवेदनशील या महत्वपूर्ण निजी तरीकों को गति देने के लिए कार्य बहुत अच्छे हैं।

उपसर्गों के नामकरण के लिए एक कन्वेंशन आपको गलती से निजी तरीकों से बचने में मदद कर सकता है (मुझे कक्षा का नाम उपसर्ग सुरक्षित लगता है)।

@interface MONObject (PrivateStuff)लोड होने पर संभावित नामकरण टकराव के कारण नामित श्रेणियां (जैसे ) विशेष रूप से अच्छा विचार नहीं हैं। वे वास्तव में केवल दोस्त या संरक्षित तरीकों के लिए उपयोगी हैं (जो कि शायद ही कभी एक अच्छा विकल्प हैं)। यह सुनिश्चित करने के लिए कि आपको अपूर्ण श्रेणी के कार्यान्वयन की चेतावनी दी गई है, आपको वास्तव में इसे लागू करना चाहिए:

@implementation MONObject (PrivateStuff)
...HERE...
@end

यहाँ एक छोटा सा एनोटेट धोखा है:

MONObject.h

@interface MONObject : NSObject

// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;

// public declaration required for clients' visibility/use.
- (void)publicMethod;

@end

MONObject.m

@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;

// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;

@end

// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
    pObject.privateBool = true;
}

@implementation MONObject
{
    bool anIvar;
}

static void AnotherPrivateMethod(MONObject * pObject) {
    if (0 == pObject) {
        assert(0 && "invalid parameter");
        return;
    }

    // if declared in the @implementation scope, you *could* access the
    // private ivars directly (although you should rarely do this):
    pObject->anIvar = true;
}

- (void)publicMethod
{
    // declared below -- but clang can see its declaration in this
    // translation:
    [self privateMethod];
}

// no declaration required.
- (void)privateMethod
{
}

- (void)MONObject_privateMethod
{
}

@end

एक और दृष्टिकोण जो स्पष्ट नहीं हो सकता है: एक सी ++ प्रकार दोनों बहुत तेज हो सकता है और निर्यात और लोड किए गए objc तरीकों की संख्या को कम करते हुए बहुत अधिक नियंत्रण प्रदान कर सकता है।


1
फ़ुल क्लास नाम को विधि नाम उपसर्ग के रूप में उपयोग करने के लिए +1! यह सिर्फ अंडरस्कोर या यहां तक ​​कि आपके अपने टीएलए से ज्यादा सुरक्षित है। (क्या होगा यदि निजी पद्धति एक पुस्तकालय में है जिसे आप अपनी किसी अन्य परियोजना में उपयोग करते हैं और आप भूल जाते हैं कि आपने पहले से ही नाम का उपयोग किया है, कभी-कभी एक या दो साल पहले ...?)
big_m

14

आप अपने कार्यान्वयन के नीचे या ऊपर एक स्थैतिक फ़ंक्शन को परिभाषित करने का प्रयास कर सकते हैं जो आपके उदाहरण के लिए एक पॉइंटर लेता है। यह आपके किसी भी इंस्टेंस चर का उपयोग करने में सक्षम होगा।

//.h file
@interface MyClass : Object
{
    int test;
}
- (void) someMethod: anArg;

@end


//.m file    
@implementation MyClass

static void somePrivateMethod (MyClass *myClass, id anArg)
{
    fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}


- (void) someMethod: (id) anArg
{
    somePrivateMethod (self, anArg);
}

@end

1
Apple अपने स्वयं के उपयोग के लिए एक प्रमुख अंडरस्कोर के साथ आरक्षित नाम रखता है।
जॉर्ज शॉली

1
और क्या होगा अगर आप Apple के ढांचे का उपयोग नहीं करते हैं? मैं अक्सर Apple के फ्रेमवर्क के बिना ऑब्जेक्टिव-सी कोड विकसित करता हूं, वास्तव में मैं लिनक्स, विंडोज और मैक ओएस एक्स पर निर्माण करता हूं। मैंने इसे वैसे भी ज्यादातर लोगों को देखते हुए हटा दिया था, जो ऑब्जेक्टिव-सी में कोड शायद मैक ओएस एक्स पर इसका इस्तेमाल करते हैं।
ड्रीमलैक्स

3
मुझे लगता है कि यह वास्तव में .m फ़ाइल में निजी विधि है। अन्य वर्ग श्रेणी की विधियाँ वास्तव में निजी नहीं हैं क्योंकि आप @interface ... @ एंड ब्लॉक के तरीकों के लिए निजी नहीं रख सकते हैं।
डेविड.छु .का

तुम ऐसा क्यों करोगे? यदि आप केवल विधि परिभाषा की शुरुआत में "-" जोड़ते हैं, तो आप एक पैरामीटर के रूप में पारित किए बिना "स्वयं" तक पहुंच पाएंगे।
गाइ

1
@Guy: क्योंकि तब विधि प्रतिबिंब द्वारा पता लगाने योग्य है, और इसलिए निजी नहीं है।
ड्रीमलैक्स

3

आप ब्लॉक का उपयोग कर सकते हैं?

@implementation MyClass

id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};

NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
    return a + b;
};

//public methods, etc.

- (NSObject) thePublicOne
{
    return createTheObject();
}

@end

मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन यह पहली बार मुझे मिला जब मैं इस बहुत ही प्रश्न का उत्तर ढूंढ रहा था। मैंने इस समाधान को कहीं और चर्चा करते हुए नहीं देखा है, इसलिए मुझे बताएं कि क्या ऐसा करने के बारे में कुछ मूर्खतापूर्ण है।


1
आपने जो यहां किया है वह एक वैश्विक ब्लॉक-प्रकार चर बना रहा है, जो वास्तव में किसी फ़ंक्शन से बेहतर नहीं है (और यह वास्तव में निजी भी नहीं है, क्योंकि यह घोषित किया गया है static)। लेकिन मैं निजी आइवर (init पद्धति से) को असाइन करने के लिए प्रयोग कर रहा हूं - थोथा जावास्क्रिप्ट-शैली - जो निजी आइवर तक पहुंच की अनुमति देता है, जो स्थिर कार्यों से संभव नहीं है। अभी तक निश्चित नहीं है जो मुझे पसंद है।
big_m

3

ऑब्जेक्टिव C में प्रत्येक ऑब्जेक्ट NSObject प्रोटोकॉल के अनुरूप है, जो कि PerformSlector: method पर आधारित है। मैं पहले भी कुछ "सहायक या निजी" तरीके बनाने की राह देख रहा था, जिन्हें मुझे सार्वजनिक स्तर पर उजागर करने की आवश्यकता नहीं थी। यदि आप बिना ओवरहेड के एक निजी विधि बनाना चाहते हैं और इसे अपनी हेडर फ़ाइल में परिभाषित नहीं करना है, तो इसे एक शॉट दें ...

नीचे दिए गए कोड के समान हस्ताक्षर के साथ अपने तरीके को परिभाषित करें ...

-(void)myHelperMethod: (id) sender{
     // code here...
}

फिर जब आपको विधि का संदर्भ देने की आवश्यकता हो तो इसे चयनकर्ता के रूप में कहें ...

[self performSelector:@selector(myHelperMethod:)];

कोड की यह पंक्ति आपके द्वारा बनाई गई विधि को लागू करेगी और हेडर फ़ाइल में इसे परिभाषित नहीं करने के बारे में एक कष्टप्रद चेतावनी है।


6
इस तरह आपके पास तीसरा पैरामीटर पास करने का कोई रास्ता नहीं है।
ली फ्यूमिन

2

यदि आप @interfaceशीर्ष पर ब्लॉक से बचना चाहते हैं , तो आप हमेशा निजी घोषणाओं को एक और फाइल में MyClassPrivate.hनहीं आदर्श रूप में रख सकते हैं, लेकिन इसके कार्यान्वयन में अव्यवस्था नहीं है।

MyClass.h

interface MyClass : NSObject {
 @private
  BOOL publicIvar_;
  BOOL privateIvar_;
}

@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end

MyClassPrivate.h

@interface MyClass ()

@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end

MyClass.m

#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass

@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;

@end

2

एक और बात जो मैंने यहाँ नहीं देखी है - Xcode नाम में "_pStreet" के साथ .h फ़ाइलों का समर्थन करता है। मान लीजिए कि आपके पास एक वर्ग MyClass है - आपके पास MyClass.m और MyClass.h है और अब आपके पास MyClass_pStreet.h भी हो सकता है। Xcode इसे पहचान लेगा और इसे सहायक संपादक में "समकक्षों" की सूची में शामिल करेगा।

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"

1

समस्या # 2 के आसपास होने का कोई तरीका नहीं है। जिस तरह से सी कंपाइलर (और इसलिए ऑब्जेक्टिव-सी कंपाइलर) काम करता है। यदि आप XCode संपादक का उपयोग करते हैं, तो फ़ंक्शन पॉपअप को फ़ाइल में नेविगेट करना @interfaceऔर @implementationब्लॉक करना आसान बनाना चाहिए ।


1

अनुपस्थिति के निजी तरीकों का लाभ है। आप तर्क को स्थानांतरित कर सकते हैं जिसे आपने अलग-अलग वर्ग में छिपाने और इसे प्रतिनिधि के रूप में उपयोग करने का इरादा किया है। इस मामले में आप प्रतिनिधि ऑब्जेक्ट को निजी के रूप में चिह्नित कर सकते हैं और यह बाहर से दिखाई नहीं देगा। तर्क को अलग वर्ग में ले जाना (शायद कई) आपके प्रोजेक्ट का बेहतर डिजाइन बनाता है। क्योंकि आपकी कक्षाएं सरल हो जाती हैं और आपके तरीकों को उचित नामों के साथ कक्षाओं में वर्गीकृत किया जाता है।


0

जैसा कि अन्य लोगों ने कहा कि @implementationअधिकांश उद्देश्यों के लिए ब्लॉक में निजी तरीकों को परिभाषित करना ठीक है।

कोड संगठन के विषय पर - मुझे pragma mark privateXcode में आसान नेविगेशन के लिए उन्हें एक साथ रखना पसंद है

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

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