ObjectiveC में चर स्थानों की घोषणा / परिभाषा?


113

जब से आईओएस ऐप और ऑब्जेक्टिव सी पर काम करना शुरू किया है, मैं वास्तव में अलग-अलग स्थानों से हैरान हूँ, जहाँ कोई भी चर घोषित और परिभाषित कर सकता है। एक तरफ हमारे पास पारंपरिक सी दृष्टिकोण है, दूसरी तरफ हमारे पास नए ऑब्जेक्टिव निर्देश हैं जो उस के ऊपर ओओ जोड़ते हैं। क्या आप लोग मुझे उन सर्वोत्तम अभ्यासों और स्थितियों को समझने में मदद कर सकते हैं, जहाँ मैं अपने चरों के लिए इन स्थानों का उपयोग करना चाहता हूँ और शायद मेरी वर्तमान समझ को सही कर सकता हूँ?

यहाँ एक नमूना वर्ग (.h और .m) है:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

तथा

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • 1 और 4 के बारे में मेरी समझ यह है कि वे सी-स्टाइल फ़ाइल-आधारित घोषणाएं और परिभाषाएं हैं जिनकी कक्षा की अवधारणा के बारे में कोई भी समझ नहीं है, और इस तरह उन्हें वास्तव में उपयोग करना होगा कि सी में उनका उपयोग कैसे किया जाएगा। पहले स्टैटिक वैरिएबल-आधारित सिंग्लेट्स को लागू करने के लिए उपयोग किया जाता है। क्या अन्य सुविधाजनक उपयोग हैं जो मुझे याद आ रहे हैं?
  • IOS के साथ काम करने से मेरा तात्पर्य यह है कि ivars को पूरी तरह से @synthesize निर्देश के बाहर चरणबद्ध रूप से बदल दिया गया है और इस तरह से ज्यादातर को अनदेखा किया जा सकता है। क्या यह मामला है?
  • 5 के बारे में: मैं कभी निजी इंटरफेस में तरीकों की घोषणा क्यों करना चाहूंगा? मेरे निजी वर्ग के तरीके इंटरफ़ेस में एक घोषणा के बिना ठीक संकलन करने लगते हैं। क्या यह ज्यादातर पठनीयता के लिए है?

धन्यवाद एक गुच्छा, दोस्तों!

जवाबों:


154

मैं आपका भ्रम समझ सकता हूं। खासकर जब से Xcode के हालिया अपडेट और नए LLVM कंपाइलर ने ivars और संपत्तियों को घोषित करने के तरीके को बदल दिया है।

"आधुनिक" उद्देश्य-सी ("पुराने" ओबज-सी 2.0 में) से पहले आपके पास बहुत सारे विकल्प नहीं थे। घुंघराले कोष्ठक के बीच हेडर में इस्तेमाल किए जाने वाले उदाहरण चर { }:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

आप इन चरों को केवल अपने कार्यान्वयन में ही सक्षम कर सकते थे, लेकिन अन्य वर्गों से नहीं। ऐसा करने के लिए, आपको एक्सेसर के तरीकों की घोषणा करनी होगी, जो कुछ इस तरह दिखाई देगा:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

इस तरह आप संदेश भेजने के लिए सामान्य वर्ग ब्रैकेट सिंटैक्स का उपयोग करके अन्य वर्गों से भी इस उदाहरण चर को प्राप्त करने और सेट करने में सक्षम थे (कॉल के तरीके):

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

क्योंकि प्रत्येक एक्सेसर विधि को मैन्युअल रूप से घोषित करना और लागू करना काफी कष्टप्रद था, @propertyऔर @synthesizeएक्सेसर विधियों को स्वचालित रूप से उत्पन्न करने के लिए पेश किया गया था:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

परिणाम बहुत स्पष्ट और कम कोड है। एक्सेसर के तरीकों को आपके लिए लागू किया जाएगा और आप पहले की तरह अभी भी ब्रैकेट सिंटैक्स का उपयोग कर सकते हैं। लेकिन इसके अलावा, आप संपत्तियों तक पहुंचने के लिए डॉट सिंटैक्स का भी उपयोग कर सकते हैं:

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Xcode 4.4 के बाद से आपको अब एक उदाहरण चर खुद घोषित करने की आवश्यकता नहीं है और आप इसे छोड़ @synthesizeभी सकते हैं। यदि आप एक आइवर घोषित नहीं करते हैं, तो कंपाइलर आपके लिए जोड़ देगा और यह आपके बिना उपयोग किए बिना एक्सेसर के तरीकों को भी उत्पन्न करेगा @synthesize

स्वतः उत्पन्न आइवर के लिए डिफ़ॉल्ट नाम एक अंडरस्कोर के साथ शुरू होने वाला नाम या आपकी संपत्ति है। आप उपयोग करके उत्पन्न आइवर का नाम बदल सकते हैं@synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

यह ठीक ऊपर दिए गए कोड की तरह काम करेगा। संगतता कारणों से आप अभी भी हेडर में ivars घोषित कर सकते हैं। लेकिन एक ही कारण है कि आप ऐसा क्यों करना चाहेंगे (और एक संपत्ति घोषित नहीं करना) एक निजी चर बनाना है, अब आप इसे कार्यान्वयन फ़ाइल में भी कर सकते हैं और यह पसंदीदा तरीका है।

@interfaceकार्यान्वयन फ़ाइल में एक ब्लॉक वास्तव में एक एक्सटेंशन है और इसे घोषित तरीकों को आगे बढ़ाने के लिए इस्तेमाल किया जा सकता है (अब ज़रूरत नहीं है) और (फिर से) गुणों की घोषणा करें। आप उदाहरण के लिए readonlyअपने हेडर में एक संपत्ति की घोषणा कर सकते हैं।

@property (nonatomic, readonly) myReadOnlyVar;

और इसे अपने कार्यान्वयन फ़ाइल में फिर readwriteसे दर्ज करें क्योंकि यह संपत्ति सिंटैक्स का उपयोग करने में सक्षम होने के लिए और न केवल ivar तक सीधी पहुंच के माध्यम से।

किसी भी @interfaceया @implementationब्लॉक के बाहर चर को पूरी तरह से घोषित करने के लिए , हाँ वे सादे सी चर हैं और बिल्कुल उसी तरह काम करते हैं।


2
बहुत बढ़िया जवाब! यह भी ध्यान दें: stackoverflow.com/questions/9859719/…
nycynik

44

सबसे पहले, @ DrummerB का उत्तर पढ़ें। यह व्हाट्स का एक अच्छा अवलोकन है और आपको आम तौर पर क्या करना चाहिए। अपने विशिष्ट प्रश्नों को ध्यान में रखते हुए:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

कोई वास्तविक परिवर्तनशील परिभाषाएँ यहाँ नहीं हैं (यह तकनीकी रूप से ऐसा करने के लिए कानूनी है यदि आप जानते हैं कि आप वास्तव में क्या कर रहे हैं, लेकिन ऐसा कभी न करें)। आप कई अन्य प्रकार की चीजों को परिभाषित कर सकते हैं:

  • typdefs
  • enums
  • externs

बाहरी परिवर्तनशील घोषणाओं की तरह दिखते हैं, लेकिन वे वास्तव में इसे कहीं और घोषित करने का वादा करते हैं। ObjC में, उन्हें केवल स्थिरांक घोषित करने के लिए उपयोग किया जाना चाहिए, और आमतौर पर केवल स्ट्रिंग स्थिरांक। उदाहरण के लिए:

extern NSString * const MYSomethingHappenedNotification;

आप तब अपनी .mफ़ाइल में वास्तविक स्थिरांक घोषित करेंगे:

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

जैसा कि DrummerB ने नोट किया, यह विरासत है। यहाँ कुछ भी मत डालो।


// 3) class-specific method / property declarations

@end

हां।


#import "SampleClass.h"

// 4) what goes here?

बाहरी स्थिरांक, जैसा कि ऊपर वर्णित है। स्टैटिक वैरिएबल भी फाइल कर सकते हैं। ये अन्य भाषाओं में वर्ग चर के बराबर हैं।


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

हां


@implementation SampleClass
{
    // 6) define ivars
}

लेकिन बहुत कम ही। लगभग हमेशा आपको क्लैंग (एक्सकोड) को आपके लिए चर बनाने की अनुमति देनी चाहिए। अपवाद आमतौर पर गैर- ObjC ivars (कोर फाउंडेशन ऑब्जेक्ट्स की तरह, और विशेष रूप से C ++ ऑब्जेक्ट्स के आसपास होते हैं यदि यह एक ObjC ++ वर्ग है), या ivars जिनके पास अजीब भंडारण शब्दार्थ हैं (जैसे ivars जो किसी कारण से संपत्ति के साथ मेल नहीं खाते हैं)।


// 7) define methods and synthesize properties from both public and private
//    interfaces

आम तौर पर आपको अब @synthesize नहीं करना चाहिए। क्लैंग (Xcode) इसे आपके लिए करेगा, और आपको इसे करने देना चाहिए।

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


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

प्रलेखन के रूप में इसका उपयोग करने में समस्या यह है कि यह वास्तव में कुछ भी दस्तावेज नहीं करता है। संश्लेषण का उपयोग करने के बावजूद, आपने एक या दोनों एक्सेसरों को ओवरराइड किया हो सकता है। वास्तव में उपयोगी कुछ भी संश्लेषित लाइन से बताने का कोई तरीका नहीं है। कोई प्रलेखन से भी बदतर एक ही चीज़ है भ्रामक प्रलेखन। इसे छोड़ दो।
रॉब नेपियर

3
# 6 दुर्लभ क्यों है? यह एक निजी चर पाने का सबसे आसान तरीका नहीं है?
pfrank

निजी संपत्ति पाने का सबसे आसान और अच्छा तरीका # 5 है।
रोब नेपियर

1
@RobNapier अभी भी कभी-कभी @ सिंथेसाइज़ का उपयोग करना आवश्यक है (जैसे कि यदि कोई संपत्ति आसानी से उसके एक्सेसर को ओवरराइड कर दी जाती है)
एंडी

6

मैं भी बहुत नया हूँ, इसलिए उम्मीद है कि मैं कुछ भी नहीं करूँगा।

1 और 4: सी-स्टाइल ग्लोबल वैरिएबल: उनके पास फ़ाइल की व्यापक गुंजाइश है। दोनों के बीच का अंतर यह है कि, चूंकि वे व्यापक हैं, पहला हैडर आयात करने वाले किसी व्यक्ति के लिए उपलब्ध होगा जबकि दूसरा नहीं है।

2: उदाहरण चर। अधिकांश उदाहरण चर को गुणों का उपयोग कर एक्सेसरों के माध्यम से संश्लेषित और पुनः प्राप्त / सेट किया जाता है क्योंकि यह मेमोरी प्रबंधन को अच्छा और सरल बनाता है, साथ ही आपको आसानी से समझने वाला डॉट नोटेशन भी देता है।

6: कार्यान्वयन ivars कुछ नए हैं। यह निजी आइवर लगाने के लिए एक अच्छी जगह है, क्योंकि आप केवल यह बताना चाहते हैं कि सार्वजनिक शीर्षक में क्या आवश्यक है, लेकिन उपवर्ग उन्हें AFAIK विरासत में नहीं मिला।

3 और 7: सार्वजनिक विधि और संपत्ति की घोषणाएं, फिर कार्यान्वयन।

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


1
ऐसा मत सोचो कि आपने कुछ भी गड़बड़ किया है :) कुछ टिप्पणियां - # 4 के साथ # 1 & # 4 esp अक्सर आप स्थिर भंडारण चर देखते हैं। # 1 अक्सर आप बाहरी भंडारण निर्दिष्ट देखेंगे और फिर # 4 में आवंटित वास्तविक भंडारण। # 2) केवल आमतौर पर अगर किसी उपवर्ग को जो भी कारण चाहिए। # 5 आगे निजी तरीकों को घोषित करने के लिए आवश्यक नहीं है।
कार्ल वीज़ेई

हाँ, मैंने खुद ही आगे की घोषणा की जाँच की। यह चेतावनी देता था कि क्या एक निजी पद्धति ने दूसरे को बुलाया जो कि आगे की घोषणा के बिना परिभाषित किया गया था, है ना? जब मैंने मुझे चेतावनी नहीं दी तो मैं आश्चर्यचकित था।
मेटाबॉल

हाँ यह संकलक का नया हिस्सा है। उन्होंने हाल ही में बहुत सारी प्रगति की है।
कार्ल वीज़े

6

यह Objective-C में घोषित किए गए सभी प्रकार के चर का एक उदाहरण है। चर नाम इसकी पहुंच का संकेत देता है।

फाइल: पशु

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

फ़ाइल: पशु

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

ध्यान दें कि iNotV अदृश्य चर किसी अन्य वर्ग से दिखाई नहीं देते हैं। यह एक दृश्यता समस्या है, इसलिए उन्हें घोषित करना @propertyया @publicइसे बदलना नहीं है।

एक निर्माता के अंदर साइड इफेक्ट से बचने के लिए @propertyअंडरस्कोर का उपयोग selfकरने के साथ घोषित चर का उपयोग करना अच्छा है ।

आइए चर का उपयोग करने का प्रयास करें।

फाइल: काउ.ह

#import "Animal.h"
@interface Cow : Animal
@end

फाइल: गाय

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

हम अभी भी रनटाइम का उपयोग करके दृश्यमान चर का उपयोग नहीं कर सकते हैं।

फ़ाइल: Cow.m (भाग 2)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

आइए, दृश्यमान चर को एक्सेस करने का प्रयास करें।

फ़ाइल: main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

यह प्रिंट करता है

iMadeVisible 
iMadeVisible2 
iMadeVisible3

ध्यान दें कि मैं बैकिंग आइवर का उपयोग करने में सक्षम था _iNotVisible2जो उपवर्ग के लिए निजी है। ऑब्जेक्टिव-सी में सभी वेरिएबल्स को पढ़ा या सेट किया जा सकता है, यहां तक ​​कि उन लोगों को भी चिह्नित किया जाता है @private, जिनका कोई अपवाद नहीं है।

मैंने संबंधित वस्तुओं या सी चर को शामिल नहीं किया क्योंकि वे अलग-अलग पक्षी हैं। C चरों के लिए, कोई भी चर बाहर परिभाषित किया गया है @interface X{}या @implementation X{}फ़ाइल चर और स्थिर भंडारण के साथ एक C चर है।

मैंने स्मृति प्रबंधन विशेषताओं, या आसानी से / readwrite, गेट्टर / सेटर विशेषताओं पर चर्चा नहीं की।

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