कहाँ "आधुनिक" उद्देश्य सी में iVars डाल करने के लिए?


82

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

लेकिन अभी तक मुझे ऐसा करने के 3 तरीके मिले। हर कोई इसे अलग तरह से कर रहा है।

1.) iVars को घुंघराले ब्रेस के एक ब्लॉक के अंदर @ कार्यान्वयन के तहत रखो (यह पुस्तक में कैसे किया जाता है)।

2.) घुंघराले ब्रेस के ब्लॉक के बिना iVars को @ कार्यान्वयन के तहत रखें

3.) @ कार्यान्वयन के ऊपर निजी इंटरफ़ेस के अंदर iVars डालें (एक वर्ग एक्सटेंशन)

ये सभी समाधान ठीक काम करने लगते हैं और अब तक मैंने अपने आवेदन के व्यवहार में कोई अंतर नहीं देखा है। मुझे लगता है कि ऐसा करने का कोई "सही" तरीका नहीं है, लेकिन मुझे कुछ ट्यूटोरियल लिखने की ज़रूरत है और मैं अपने कोड के लिए केवल एक ही रास्ता चुनना चाहता हूं।

मुझे किस रास्ते पर जाना चाहिए?

संपादित करें: मैं केवल यहाँ iVars के बारे में बात कर रहा हूँ। गुण नहीं। केवल अतिरिक्त चर ही वस्तु को केवल अपने लिए चाहिए और जिसे बाहर की ओर उजागर नहीं किया जाना चाहिए।

कोड नमूने

1)

#import "Person.h"

@implementation Person
{
    int age;
    NSString *name;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

2)

#import "Person.h"

@implementation Person

int age;
NSString *name;


- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

3)

#import "Person.h"

@interface Person()
{
    int age;
    NSString *name;
}
@end

@implementation Person

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

4
"सही" पर कोई टिप्पणी नहीं, लेकिन एक अन्य विकल्प - जो मुझे लगता है कि आगे बढ़ रहा है - पूरी तरह से iVars को डंप कर रहा है और सब कुछ एक संपत्ति बना रहा है (ज्यादातर एक वर्ग विस्तार के भीतर .m फ़ाइल में)। संगति मुझे राज्य के कार्यान्वयन विवरण के बारे में सोचने से बचाती है।
फिलिप मिल्स

1
यदि आप 3 कोड विकल्पों में से प्रत्येक का वास्तविक कोड उदाहरण दिखाने के लिए प्रश्न को अपडेट करते हैं, तो यह प्रश्न वास्तव में दूसरों को लाभान्वित करेगा। मैं व्यक्तिगत रूप से केवल विकल्प 1 का उपयोग करता हूं, विकल्प 3 को देखा है, लेकिन मैं विकल्प 2 से परिचित नहीं हूं।
तेजस्वी

1
धन्यवाद मैडी। मैंने कुछ सैंपल कोड जोड़े।
वार्ता

4
क्षमा करें, बस अपडेट देखा गया। विकल्प 2 वैध नहीं है। यह ivars नहीं बनाता है। यह फ़ाइल वैश्विक चर बनाता है। उस वर्ग का हर उदाहरण चर का एक सेट साझा करेगा। आप उन्हें कक्षा चर सकते हैं, उदाहरण चर नहीं।
रामादेवी

जवाबों:


162

@implementationब्लॉक में या एक वर्ग विस्तार में उदाहरण चर डालने की क्षमता , "आधुनिक उद्देश्य-सी रनटाइम" की एक विशेषता है, जिसका उपयोग आईओएस के प्रत्येक संस्करण और 64-बिट मैक ओएस एक्स कार्यक्रमों द्वारा किया जाता है।

यदि आप 32-बिट मैक ओएस एक्स एप्लिकेशन लिखना चाहते हैं, तो आपको अपने उदाहरण चर को @interfaceघोषणा में रखना होगा । संभावना है कि आपको अपने ऐप के 32-बिट संस्करण का समर्थन करने की आवश्यकता नहीं है, हालांकि। ओएस एक्स ने संस्करण 10.5 (तेंदुए) के बाद से 64-बिट ऐप्स का समर्थन किया है, जो पांच साल पहले जारी किया गया था।

तो, चलिए मान लेते हैं कि आप केवल ऐसे ऐप्स लिख रहे हैं जो आधुनिक रनटाइम का उपयोग करेंगे। आपको अपने ivars कहां रखना चाहिए?

विकल्प 0: में @interface(यह मत करो)

सबसे पहले, आइए हम एक घोषणा में उदाहरण चर क्यों नहीं डालना चाहते हैं @interface

  1. @interfaceकक्षा के उपयोगकर्ताओं को क्रियान्वयन के विवरणों में उदाहरण चर डालते हुए । कार्यान्वयन के विवरणों पर भरोसा करने के लिए यह उन उपयोगकर्ताओं (यहां तक ​​कि अपनी कक्षाओं का उपयोग करते समय भी!) का नेतृत्व कर सकता है जो उन्हें नहीं करना चाहिए। (यह इस बात से स्वतंत्र है कि क्या हम ivars घोषित करते हैं @private।)

  2. एक @interfaceसंकलन में उदाहरण के चर को अधिक समय लगता है, क्योंकि किसी भी समय हम एक आइवर घोषणा को जोड़ते हैं, बदलते हैं या हटाते हैं, हमें .mइंटरफ़ेस आयात करने वाली प्रत्येक फ़ाइल को फिर से जोड़ना होगा ।

इसलिए हम उदाहरण चर नहीं रखना चाहते हैं @interface। हमें उन्हें कहां रखना चाहिए?

विकल्प 2: @implementationबिना ब्रेसिज़ में (यह मत करो)

इसके बाद, अपने विकल्प 2 पर चर्चा करें, "घुंघराले ब्रेस के ब्लॉक के बिना @ आइवर्स लागू करें"। यह उदाहरण चर घोषित नहीं करता है ! आप इस बारे में बात कर रहे हैं:

@implementation Person

int age;
NSString *name;

...

यह कोड दो वैश्विक चर को परिभाषित करता है। यह किसी भी उदाहरण चर घोषित नहीं करता है।

उदाहरण के लिए, यदि आपके पास वैश्विक चर की जरूरत है, तो यह आपकी .mफ़ाइल में वैश्विक चर को परिभाषित करने के लिए भी ठीक है @implementation, क्योंकि आप चाहते हैं कि आपके सभी उदाहरण कुछ राज्य को साझा करें, जैसे कि कैश। लेकिन आप इस विकल्प का उपयोग ivars घोषित करने के लिए नहीं कर सकते, क्योंकि यह ivars घोषित नहीं करता है। (इसके अलावा, आपके कार्यान्वयन के लिए निजी वैश्विक चर आमतौर पर staticवैश्विक नाम स्थान को प्रदूषित करने और लिंक-टाइम त्रुटियों को जोखिम में डालने से बचने के लिए घोषित किया जाना चाहिए ।)

यह आपके विकल्प 1 और 3 को छोड़ देता है।

विकल्प 1: @implementationब्रेसिज़ के साथ (इसे करें)

आमतौर पर हम विकल्प 1 का उपयोग करना चाहते हैं: उन्हें अपने मुख्य @implementationब्लॉक में, ब्रेसिज़ में रखें, जैसे:

@implementation Person {
    int age;
    NSString *name;
}

हमने उन्हें यहां रखा क्योंकि यह उनके अस्तित्व को निजी रखता है, मैंने पहले बताई गई समस्याओं को रोका, और क्योंकि आमतौर पर उन्हें कक्षा विस्तार में रखने का कोई कारण नहीं है।

तो जब हम आपके विकल्प 3 का उपयोग करना चाहते हैं, तो उन्हें क्लास एक्सटेंशन में डाल दें?

विकल्प 3: एक क्लास एक्सटेंशन में (केवल आवश्यक होने पर ही करें)

क्लास के एक्सटेंशन में उन्हें क्लास एक्सटेंशन में रखने का लगभग कोई कारण नहीं है @implementation। हम @implementationउस मामले में भी उन्हें डाल सकते हैं ।

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

// UICollectionView.h

@interface UICollectionView : UIScrollView

- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
// etc.

@end

@interface UICollectionView (ReusableViews)

- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;

- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;

- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;

@end

ठीक है, अब हम मुख्य UICollectionViewतरीकों UICollectionView.mको लागू कर सकते हैं और हम उन तरीकों को लागू कर सकते हैं जो पुन: प्रयोज्य विचारों को प्रबंधित करते हैं UICollectionView+ReusableViews.m, जो हमारे स्रोत कोड को थोड़ा अधिक प्रबंधनीय बनाता है।

लेकिन हमारे पुन: प्रयोज्य दृश्य प्रबंधन कोड को कुछ उदाहरण चर की आवश्यकता होती है। उन चरों को मुख्य वर्ग @implementationमें उजागर किया जाना है UICollectionView.m, इसलिए संकलक उन्हें .oफ़ाइल में उत्सर्जित करेगा । और हमें कोड में उन उदाहरण चर को उजागर करने की भी आवश्यकता है UICollectionView+ReusableViews.m, इसलिए वे विधियां आइवर का उपयोग कर सकती हैं।

यह वह जगह है जहाँ हमें एक वर्ग विस्तार की आवश्यकता है। हम एक निजी हेडर फ़ाइल में एक वर्ग विस्तार में पुन: प्रयोज्य-दृश्य-प्रबंधन ivars डाल सकते हैं:

// UICollectionView_ReusableViewsSupport.h

@interface UICollectionView () {
    NSMutableDictionary *registeredCellSources;
    NSMutableDictionary *spareCellsByIdentifier;

    NSMutableDictionary *registeredSupplementaryViewSources;
    NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}

- (void)initReusableViewSupport;

@end

हम इस हेडर फ़ाइल को अपने लाइब्रेरी के उपयोगकर्ताओं के लिए शिप नहीं करेंगे। हम इसे केवल इन UICollectionView.mऔर इंपोर्ट करेंगे UICollectionView+ReusableViews.m, ताकि इन आइवर को देखने की जरूरत की हर चीज इन्हें देख सके। हमने एक विधि में भी फेंक दिया है जिसे हम initपुन: प्रयोज्य-दृश्य-प्रबंधन कोड को प्रारंभ करने के लिए कॉल करने के लिए मुख्य विधि चाहते हैं । हम उस विधि को -[UICollectionView initWithFrame:collectionViewLayout:]अंदर से कॉल करेंगे UICollectionView.m, और हम इसे लागू करेंगे UICollectionView+ReusableViews.m


4
विकल्प 3: यह भी प्रयोग करने योग्य है जब 1) आप अपने स्वयं के कार्यान्वयन (@property int age) और 2) के भीतर भी अपने ivars के लिए संपत्तियों का उपयोग करना चाहते हैं, तो आप आसानी से सार्वजनिक संपत्ति (@property (आसानी से) int उम्र) को ओवरराइड करना चाहते हैं आपके शीर्षलेख में आपके कोड को कार्यान्वयन में रीडराइट के रूप में संपत्ति का उपयोग करने की अनुमति देता है (@property (रीडराइट) इंट आयु)। जब तक ऐसा करने के अन्य तरीके नहीं हैं, @rob mayoff?
दुबला

@ लीन यदि आप readonlyनिजी रूप से होने वाली संपत्ति को ओवरराइड करना चाहते हैं readwrite, तो आपको एक क्लास एक्सटेंशन का उपयोग करने की आवश्यकता है। लेकिन सवाल यह था कि आइवर कहां लगाएं, प्रॉपर्टी डिक्लेरेशन कहां लगाएं। मैं यह नहीं देखता कि एक वर्ग विस्तार का उपयोग कैसे कार्यान्वयन को आइवर तक पहुंचने से रोक सकता है। ऐसा करने के लिए, आपको अपनी पद्धति के कार्यान्वयन को एक श्रेणी में रखना होगा, क्योंकि कंपाइलर को देखने से पहले सभी ivar-घोषित वर्ग एक्सटेंशनों को देखना होगा @implementation
रोब मेफ

@rob mayoff, सही और सच्चा - आप वैध अंक बनाते हैं। होली ने निर्दिष्ट किया कि गुण यहां प्रश्न पर नहीं थे, और "एनफोर्स" उनके उपयोग के बारे में मेरे हिस्से पर थोड़ा मजबूत था। भले ही, अगर कोई ऐसा करने के बजाय सीधे अपने ivars तक पहुंचने के लिए गुणों का उपयोग करना चाहता है, तो वह ऐसा करने का तरीका होगा। और निश्चित रूप से आप अभी भी '_', (_age = someAge) का उपयोग करके सीधे आइवर का उपयोग कर सकते हैं। मैंने टिप्पणी को इसलिए जोड़ा क्योंकि मुझे लगा कि संपत्ति का उपयोग ivars पर चर्चा करते समय ध्यान देने योग्य है, क्योंकि कई लोग उन्हें एक साथ उपयोग करते हैं, और यह ऐसा करने का तरीका होगा।
22

वह है "विकल्प 0: @ में (यह मत करो)"।
रॉ मयऑफ

5

विकल्प 2 गलत है। वे वैश्विक चर हैं, उदाहरण चर नहीं।

विकल्प 1 और 3 अनिवार्य रूप से समान हैं। यह बिल्कुल कोई फर्क नहीं पड़ता।

पसंद है कि हेडर फ़ाइल या कार्यान्वयन फ़ाइल में उदाहरण चर डालें। हेडर फ़ाइल का उपयोग करने का लाभ यह है कि आपके पास अपने इंस्टेंस चर और इंटरफ़ेस घोषणा को देखने और संपादित करने के लिए एक त्वरित और आसान कीबोर्ड शॉर्टकट (कमांड + कंट्रोल + अप इन Xcode) है।

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

कार्यान्वयन उदाहरण चर कुछ स्थितियों के लिए बहुत अच्छा विकल्प हैं, लेकिन मेरे अधिकांश कोड के लिए मैं अभी भी हेडर में उदाहरण चर को केवल इसलिए डाल देता हूं क्योंकि यह मेरे लिए Xcode में काम करने वाले कोडर के रूप में अधिक सुविधाजनक है। मेरी सलाह है कि आप जो कुछ भी महसूस करते हैं वह आपके लिए अधिक सुविधाजनक है।


4

बड़े पैमाने पर इसका तात्पर्य आइवर की उप-श्रेणियों से दृश्यता से है। उपवर्ग @implementationब्लॉक में परिभाषित उदाहरण चर तक पहुंचने में सक्षम नहीं होंगे ।

पुन: प्रयोज्य कोड के लिए जिसे मैं वितरित करने की योजना बना रहा हूं (उदाहरण के लिए लाइब्रेरी या फ्रेमवर्क कोड), जहां मैं सार्वजनिक निरीक्षण के लिए उदाहरण चर को उजागर नहीं करना चाहता हूं, तो मैं कार्यान्वयन ब्लॉक (आपके विकल्प 1) में ivars रखने के लिए इच्छुक हूं।


3

आपको क्रियान्वयन के ऊपर एक निजी इंटरफ़ेस में उदाहरण चर रखना चाहिए। विकल्प 3।

इस पर पढने के लिए दस्तावेज ऑब्जेक्टिव-सी गाइड में प्रोग्रामिंग है

प्रलेखन से:

आप गुण के बिना उदाहरण चर को परिभाषित कर सकते हैं

किसी भी समय किसी मूल्य या किसी अन्य वस्तु का ट्रैक रखने के लिए किसी वस्तु पर किसी संपत्ति का उपयोग करना सबसे अच्छा अभ्यास है।

यदि आपको किसी संपत्ति की घोषणा किए बिना अपने स्वयं के उदाहरण चर को परिभाषित करने की आवश्यकता है, तो आप उन्हें क्लास इंटरफ़ेस या कार्यान्वयन के शीर्ष पर ब्रेसिज़ के अंदर जोड़ सकते हैं, जैसे:


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

1

सार्वजनिक ivars को @interface में संपत्तियों को घोषित किया जाना चाहिए (संभावना है कि आप 1 में क्या सोच रहे हैं)। निजी आइवर, यदि आप नवीनतम Xcode चला रहे हैं और आधुनिक रनटाइम (64-बिट OS X या iOS) का उपयोग कर रहे हैं, तो क्लास एक्सटेंशन के बजाय, @ कार्यान्वयन (2) में घोषित किया जा सकता है, जो कि संभवतः आप ' 3 में फिर से सोचना।

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