क्या आईडी के बजाय इंस्टेंटाइप का उपयोग करना शुरू करना फायदेमंद होगा?


226

क्लैंग एक कीवर्ड जोड़ता instancetypeहै, जहां तक ​​मैं देख सकता हूं, idएक रिटर्न प्रकार के रूप में -allocऔर में बदल देता है init

के instancetypeबजाय का उपयोग करने के लिए एक लाभ है id?


10
नहीं .. आवंटित और init के लिए नहीं, क्योंकि वे पहले से ही इस तरह काम करते हैं। इंस्टेंक्टाइप की बात इतनी है कि आप व्यवहारिकता की तरह कस्टम तरीके आवंटित / इनिट दे सकते हैं।
hooleyhoop

@hooleyhoop वे इस तरह से काम नहीं करते हैं। वे आइडी वापस करते हैं। आईडी 'ओब्ज-सी ऑब्जेक्ट' है। बस इतना ही। संकलक जानता है कि इसके अलावा अन्य वापसी मूल्य के बारे में कुछ भी नहीं है।
ग्रिप्सस्पी

5
वे इस तरह से काम करते हैं, अनुबंध और प्रकार की जाँच पहले से ही init के लिए हो रही है, केवल कस्ट्रक्टर्स के लिए आपको इसकी आवश्यकता है। nshipster.com/instancetype
kocodude

चीजें काफी आगे बढ़ चुकी हैं, हां। संबंधित परिणाम प्रकार अपेक्षाकृत नए हैं और अन्य परिवर्तनों का मतलब है कि यह जल्द ही एक बहुत ही स्पष्ट मुद्दा होगा।
griotspeak

1
मैं सिर्फ टिप्पणी करना चाहते हैं कि अब iOS 8 कि वापस जाने के लिए इस्तेमाल किया तरीकों का एक बहुत पर idसाथ प्रतिस्थापित किया गया है instancetype, यहाँ तक कि initसे NSObject। यदि आप आपको कोड का उपयोग तेजी के साथ करना चाहते हैं, तो आपको उपयोग करना होगाinstancetype
होला सो ईदु फेलिज नवाडीज़

जवाबों:


192

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

केवल इसका उपयोग करें जहां यह निश्चित रूप से समझ में आता है (यानी एक विधि जो उस वर्ग का एक उदाहरण लौटा रही है); आईडी अभी भी उपयोगी है।


8
alloc, initआदि instancetypeसंकलक द्वारा स्वचालित रूप से प्रचारित होते हैं । यह कहना है कि कोई लाभ नहीं है; वहाँ है, लेकिन यह ऐसा नहीं है।
स्टीवन फिशर

10
यह सुविधा निर्माण करने वालों के लिए अधिक उपयोगी है
कैटफ़िश_मन

5
यह 2011 में लॉयन की रिहाई के साथ एआरसी (और समर्थन में मदद करने के लिए) के साथ पेश किया गया था। कोको में व्यापक रूप से अपनाने की एक बात यह है कि सभी उम्मीदवारों को यह देखने के लिए ऑडिट करना होगा कि वे क्या करते हैं [NameOfClass] बजाय आबंटित करें], क्योंकि यह करने के लिए सुपर भ्रामक होगा [SomeSubClass फेसिलिटी कंस्ट्रक्टर], के साथ + सुविधाकंस्ट्रक्टर ने इंस्टेंटाइप को वापस करने की घोषणा की, और यह SomeSubClass का एक उदाहरण नहीं लौटा है।
कैटफ़िश_मन

2
'आईडी' केवल इंस्टेंक्टाइप में परिवर्तित किया जाता है जब कंपाइलर विधि परिवार को अनुमान लगा सकता है। यह निश्चित रूप से सुविधा निर्माणकर्ताओं, या अन्य आईडी-रिटर्निंग विधियों के लिए ऐसा नहीं करता है।
कैटफ़िश_मैन

4
ध्यान दें कि आईओएस 7 में, फाउंडेशन में कई तरीकों को इंस्टेंटाइप में परिवर्तित किया गया है। मैंने व्यक्तिगत रूप से गलत पूर्व-मौजूदा कोड के कम से कम 3 मामलों को पकड़ा है।
कैटफ़िश_मैन

336

हां, instancetypeउन सभी मामलों में उपयोग करने के लाभ हैं जहां यह लागू होता है। मैं और अधिक विस्तार से समझाता हूं, लेकिन मुझे इस साहसिक कथन के साथ शुरू करने दें: instancetypeजब भी उपयुक्त हो, तब उपयोग करें , जब भी कोई वर्ग उसी वर्ग का उदाहरण देता है।

वास्तव में, इस विषय पर एप्पल अब क्या कहता है:

अपने कोड में, जहां उचित हो, idवापसी मूल्य के रूप में होने वाली घटनाओं को बदलें instancetype। यह आमतौर पर initतरीकों और वर्ग कारखाने के तरीकों के लिए मामला है । भले ही संकलक स्वचालित रूप से उन विधियों को परिवर्तित करता है जो "आवंटित," "init," या "नए" से शुरू होती हैं और वापसी का प्रकार होता idहै instancetype, यह अन्य विधियों को परिवर्तित नहीं करता है। उद्देश्य-सी सम्मेलन instancetypeसभी विधियों के लिए स्पष्ट रूप से लिखना है ।

उस रास्ते से, चलो आगे बढ़ते हैं और समझाते हैं कि यह एक अच्छा विचार क्यों है।

सबसे पहले, कुछ परिभाषाएँ:

 @interface Foo:NSObject
 - (id)initWithBar:(NSInteger)bar; // initializer
 + (id)fooWithBar:(NSInteger)bar;  // class factory
 @end

एक वर्ग कारखाने के लिए, आपको हमेशा उपयोग करना चाहिए instancetype। संकलक स्वचालित रूप से परिवर्तित नहीं होता idहै instancetype। वह idएक सामान्य वस्तु है। लेकिन अगर आप इसे बनाते हैं तो instancetypeसंकलक जानता है कि विधि किस प्रकार की वस्तु देती है।

यह एक शैक्षणिक समस्या नहीं है। उदाहरण के लिए, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]मैक ओएस एक्स ( केवल ) पर एक त्रुटि उत्पन्न करेगा , जिसे 'राइटडैट:' नाम के कई तरीके बेमेल परिणाम, पैरामीटर प्रकार या विशेषताओं के साथ मिलते हैं । कारण यह है कि दोनों NSFileHandle और NSURLHandle प्रदान करते हैं writeData:। चूंकि [NSFileHandle fileHandleWithStandardOutput]रिटर्न idए, संकलक निश्चित नहीं है कि किस वर्ग writeData:को बुलाया जा रहा है।

आपको इसके चारों ओर काम करने की आवश्यकता है:

[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];

या:

NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];

बेशक, बेहतर उपाय यह है कि रिटर्न ए घोषित fileHandleWithStandardOutputकिया जाए instancetype। फिर कास्ट या असाइनमेंट आवश्यक नहीं है।

(ध्यान दें कि iOS पर, यह उदाहरण केवल एक त्रुटि NSFileHandleप्रदान नहीं करेगा क्योंकि केवल एक ही writeData:उदाहरण उपलब्ध कराता है । अन्य उदाहरण मौजूद हैं, जैसे कि lengthएक CGFloatसे UILayoutSupportलेकिन एक NSUIntegerसे वापस NSString।)

नोट : जब से मैंने यह लिखा है, macOS हेडर को एक के NSFileHandleबजाय वापस करने के लिए संशोधित किया गया है id

शुरुआती के लिए, यह अधिक जटिल है। जब आप यह टाइप करें:

- (id)initWithBar:(NSInteger)bar

... संकलक आपको इसके बजाय टाइप किया हुआ दिखाएगा:

- (instancetype)initWithBar:(NSInteger)bar

एआरसी के लिए यह आवश्यक था। यह क्लैंग भाषा एक्सटेंशन संबंधित परिणाम प्रकारों में वर्णित है । यही कारण है कि लोग आपको बताएंगे कि इसका उपयोग करना आवश्यक नहीं है instancetype, हालांकि मुझे लगता है कि आपको चाहिए। इस उत्तर का शेष भाग इससे संबंधित है।

तीन फायदे हैं:

  1. स्पष्ट। आपका कोड वही कर रहा है जो वह कहता है, बजाय कुछ और।
  2. पैटर्न। आप कई बार अच्छी आदतों का निर्माण कर रहे हैं जो मायने रखती है, जो मौजूद हैं।
  3. संगति। आपने अपने कोड में कुछ स्थिरता स्थापित की है, जो इसे और अधिक पठनीय बनाता है।

मुखर

यह सच है कि ए से लौटने का कोई तकनीकी लाभ नहीं है । लेकिन यह इसलिए है क्योंकि संकलक स्वचालित रूप से करने के लिए कनवर्ट करता है । आप इस विचित्रता पर भरोसा कर रहे हैं; जब आप लिख रहे हैं कि रिटर्न ए, संकलक इसे व्याख्या कर रहा है जैसे कि यह रिटर्न देता है ।instancetypeinitidinstancetypeinitidinstancetype

ये संकलक के बराबर हैं :

- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;

ये आपकी आंखों के बराबर नहीं हैं। सबसे अच्छा, आप अंतर को अनदेखा करना सीखेंगे और उस पर स्किम करेंगे। यह कुछ ऐसा नहीं है जिसे आपको अनदेखा करना सीखना चाहिए।

पैटर्न

जबकि initअन्य तरीकों के साथ कोई अंतर नहीं है , जैसे ही आप एक क्लास फैक्ट्री को परिभाषित करते हैं, तो अंतर होता है

ये दोनों समतुल्य नहीं हैं:

+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

आप दूसरा रूप चाहते हैं। यदि आप instancetypeएक निर्माता के रिटर्न प्रकार के रूप में टाइप करने के लिए उपयोग किए जाते हैं , तो आपको यह हर बार सही मिलेगा।

संगति

अंत में, कल्पना करें कि क्या आप इसे एक साथ रखते हैं: आप एक initफंक्शन चाहते हैं और एक क्लास फैक्ट्री भी।

यदि आप के लिए उपयोग idकरते हैं init, तो आप इस तरह कोड के साथ समाप्त होते हैं:

- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

लेकिन अगर आप उपयोग करते हैं instancetype, तो आपको यह मिलता है:

- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

यह अधिक सुसंगत और अधिक पठनीय है। वे उसी चीज को वापस करते हैं, और अब यह स्पष्ट है।

निष्कर्ष

जब तक आप जानबूझकर पुराने कंपाइलरों के लिए कोड नहीं लिख रहे हैं, तब तक आपको instancetypeउचित उपयोग करना चाहिए ।

आपको एक संदेश लिखने से पहले संकोच करना चाहिए जो रिटर्न करता है id। अपने आप से पूछें: क्या यह इस वर्ग का एक उदाहरण है? यदि हां, तो यह एक है instancetype

निश्चित रूप से ऐसे मामले हैं जहां आपको वापस जाने की आवश्यकता है id, लेकिन आप शायद instancetypeअधिक बार उपयोग करेंगे ।


4
मुझे यह पूछे हुए कुछ समय हो गया है और मैं बहुत समय से एक रुख अपनाए हुए हूं, जैसा कि आपने यहां रखा है। मैंने इसे यहां जाने दिया क्योंकि मुझे लगता है कि, दुर्भाग्यवश, यह init के लिए शैली का एक मुद्दा माना जाएगा और पहले की प्रतिक्रियाओं में प्रस्तुत जानकारी ने इस प्रश्न का उत्तर स्पष्ट रूप से दिया।
ग्रिप्सस्पीक

6
स्पष्ट होने के लिए: मेरा मानना ​​है कि कैटफ़िश_मन का उत्तर केवल अपने पहले वाक्य में सही है । Hooleyhoop का उत्तर इसके पहले वाक्य को छोड़कर सही है । यह एक दुविधा का नरक है; मैं कुछ ऐसा प्रदान करना चाहता था, जो समय के साथ-साथ और भी अधिक उपयोगी और सही हो। (उन दोनों के लिए उचित सम्मान के साथ, आखिरकार, यह तब और अधिक स्पष्ट है जब यह वापस आ गया था जब उनके उत्तर लिखे गए थे।)
स्टीवन फिशर

1
समय के साथ मुझे उम्मीद है। मैं किसी भी कारण से नहीं सोच सकता, जब तक कि ऐप्पल को लगता है कि स्रोत के साथ स्रोत संगतता बनाए रखना महत्वपूर्ण है (उदाहरण के लिए) एनएसएसट्रिंग को बिना कलाकारों के एक नया NSArray असाइन करना।
स्टीवन फिशर

4
instancetypeबनाम idवास्तव में एक शैली निर्णय नहीं है। लगभग हाल ही में हुए बदलावों से instancetypeयह स्पष्ट होता है कि हमें उन instancetypeजगहों पर उपयोग करना चाहिए , -initजहाँ हमारा मतलब है 'मेरी कक्षा का एक उदाहरण'
griotspeak

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

10

इस प्रश्न को समझाने के लिए उपरोक्त उत्तर पर्याप्त से अधिक हैं। मैं पाठकों को कोडिंग के संदर्भ में समझने के लिए एक उदाहरण जोड़ना चाहूंगा।

कक्षा

@interface ClassA : NSObject

- (id)methodA;
- (instancetype)methodB;

@end

कक्षा बी

@interface ClassB : NSObject

- (id)methodX;

@end

TestViewController.m

#import "ClassA.h"
#import "ClassB.h"

- (void)viewDidLoad {

    [[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime

    [[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible @interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}

यह केवल एक संकलक त्रुटि उत्पन्न करेगा यदि आप #import "ClassB.h" का उपयोग नहीं करते हैं। अन्यथा, संकलक मान लेंगे कि आप जानते हैं कि आप क्या कर रहे हैं। उदाहरण के लिए, निम्नलिखित संकलन, लेकिन रनटाइम पर क्रैश: आईडी myNumber = @ (1); आईडी iAssumedThatWasAnArray = myNumber [0]; आईडी iAssumedThatWasADEDIA = [myNumber objectForKey: @ "कुंजी"];
OlDor

1

तुम भी नामित प्रारंभिक में विस्तार से प्राप्त कर सकते हैं

**

INSTANCETYPE

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

आईडी

** उद्देश्य-सी में इंस्टेंटेट पेश किए जाने से पहले, इनिशियलाइज़र्स रिटर्न आईडी (आँख- dee)। इस प्रकार को "किसी भी वस्तु के लिए एक संकेतक" के रूप में परिभाषित किया गया है। (आईडी सी में शून्य की तरह एक बहुत है।) इस लेखन के रूप में, XCode क्लास टेम्प्लेट अभी भी आईडी का उपयोग बॉयलरप्लेट कोड में जोड़े गए शुरुआती प्रकार के रिटर्न के रूप में करते हैं। इंस्टेंटाइप के विपरीत, आईडी का उपयोग केवल रिटर्न प्रकार से अधिक के रूप में किया जा सकता है। जब आप अनिश्चित होते हैं कि आप किस प्रकार के ऑब्जेक्ट को इंगित करेंगे तो वेरिएबल का वैरिएबल या विधि पैरामीटर घोषित कर सकते हैं। जब आप एक से अधिक या अनजाने प्रकार की वस्तुओं की एक सरणी पर पुनरावृति करने के लिए तेज़ गणना का उपयोग करते हुए आईडी का उपयोग कर सकते हैं। ध्यान दें कि क्योंकि आईडी "किसी भी वस्तु के लिए एक संकेतक" के रूप में अपरिभाषित है, तो आप इस प्रकार के एक चर या ऑब्जेक्ट पैरामीटर की घोषणा करते समय एक * शामिल नहीं करते हैं।

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