ऑब्जेक्टिव-सी में एक अमूर्त वर्ग बनाना


507

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

यदि नहीं, तो मैं ऑब्जेक्टिव-सी में एक अमूर्त वर्ग के कितने करीब पहुँच सकता हूँ?


18
नीचे दिए गए जवाब बहुत अच्छे हैं। मुझे लगता है कि यह अमूर्त वर्गों का मुद्दा निजी तौर पर निजी तरीकों से संबंधित है - दोनों ग्राहक कोड को प्रतिबंधित करने के तरीके हैं, और न ही उद्देश्य-सी में मौजूद हैं। मुझे लगता है कि यह समझने में मदद मिलती है कि भाषा की मानसिकता स्वयं जावा से मौलिक रूप से भिन्न है। मेरा उत्तर देखें: stackoverflow.com/questions/1020070/#1020330
क्विन टेलर

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

1
तो कोकोदेव साइट पर एक नज़र डालें जो इसे एक जावा तुलना देता है cocoadev.com/index.pl?AbstractSuperClass

2
हालाँकि बैरी ने एक सोचा के रूप में इसका उल्लेख किया है (मुझे माफ कर दो अगर मैं इसे गलत पढ़ रहा हूँ), मुझे लगता है कि आप उद्देश्य सी में एक प्रोटोकॉल की तलाश कर रहे हैं । उदाहरण के लिए, एक प्रोटोकॉल क्या है?
3

जवाबों:


633

आमतौर पर, ऑब्जेक्टिव-सी क्लास केवल कन्वेंशन द्वारा अमूर्त होती है - यदि लेखक किसी क्लास को एब्सट्रैक्ट के रूप में डॉक्यूमेंट करता है, तो उसे बिना सब-क्लास किए इस्तेमाल न करें। कोई संकलन-समय प्रवर्तन नहीं है, जो अमूर्त वर्ग के तात्कालिकता को रोकता है। वास्तव में, किसी उपयोगकर्ता को एक श्रेणी (यानी रनटाइम पर) के माध्यम से अमूर्त तरीकों के कार्यान्वयन प्रदान करने से रोकना नहीं है। आप एक उपयोगकर्ता को अपने अमूर्त वर्ग में उन तरीकों को लागू करने में अपवाद को बढ़ाकर कम से कम कुछ तरीकों से बाध्य कर सकते हैं:

[NSException raise:NSInternalInconsistencyException 
            format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];

यदि आपकी विधि कोई मान लौटाती है, तो इसका उपयोग करना थोड़ा आसान है

@throw [NSException exceptionWithName:NSInternalInconsistencyException
                               reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
                             userInfo:nil];

तब तक आपको विधि से रिटर्न स्टेटमेंट जोड़ने की आवश्यकता नहीं है।

यदि अमूर्त वर्ग वास्तव में एक इंटरफ़ेस है (अर्थात कोई ठोस विधि कार्यान्वयन नहीं है), तो Objective-C प्रोटोकॉल का उपयोग करना अधिक उपयुक्त विकल्प है।


18
मुझे लगता है कि उत्तर का सबसे उपयुक्त हिस्सा यह उल्लेख कर रहा था कि आप केवल परिभाषित तरीकों के बजाय @protocol का उपयोग कर सकते हैं।
चाड स्टीवर्ट

13
स्पष्ट करने के लिए: आप किसी परिभाषा में विधियों की घोषणा@protocol कर सकते हैं, लेकिन आप वहां विधियों को परिभाषित नहीं कर सकते।
रिचर्ड

NSException पर एक श्रेणी पद्धति के साथ + (instancetype)exceptionForCallingAbstractMethod:(SEL)selectorयह बहुत अच्छी तरह से काम करता है।
पैट्रिक पिजनपेल

यह मेरे लिए तब से काम कर रहा है, IMHO, एक अपवाद को फेंकना अन्य डेवलपर्स के लिए अधिक स्पष्ट है कि यह एक वांछित व्यवहार से अधिक है doesNotRecognizeSelector
क्रिस

प्रोटोकॉल का उपयोग करें (पूरी तरह से अमूर्त वर्ग के लिए) या टेम्पलेट विधि पैटर्न जहां सार वर्ग में आंशिक कार्यान्वयन / प्रवाह तर्क है जैसा कि यहां दिया गया है stackoverflow.com/questions/8146439/… । नीचे मेरा जवाब देखें।
हयदतुल्लाह

267

नहीं, ऑब्जेक्टिव-सी में अमूर्त वर्ग बनाने का कोई तरीका नहीं है।

आप एक अमूर्त वर्ग का मजाक उड़ा सकते हैं - विधियों / चयनकर्ताओं को doNotRecognizeSelector कॉल करके: और इसलिए कक्षा को अनुपयोगी बनाते हुए एक अपवाद बढ़ाएं।

उदाहरण के लिए:

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

आप इनिट के लिए भी ऐसा कर सकते हैं।


5
@ चक, मैंने इसे अस्वीकार नहीं किया, लेकिन NSObjectसंदर्भ यह बताता है कि आप इस पद्धति का उपयोग नहीं करना चाहते हैं, न कि किसी विधि को ओवरराइड करने के लिए। हालाँकि वे एक ही बात हो सकती है, शायद :)
Dan Rosenstark

आप इस उदाहरण में केवल एक प्रोटोकॉल का उपयोग क्यों नहीं करना चाहेंगे? मेरे लिए, यह केवल विधि स्टब्स के लिए जानना अच्छा है, लेकिन पूरे सार वर्ग के लिए नहीं। इसके लिए उपयोग का मामला सीमित लगता है।
lewiguez

मैंने इसे अस्वीकार नहीं किया, लेकिन सुझाव है कि आपको एक अपवाद का कारण बनना चाहिए जो कि init विधि में उठाया गया है, इसका सबसे संभावित कारण है। एक उपवर्ग के लिए सबसे सामान्य प्रारूप स्वयं = [सुपर इनिट] कहकर अपनी खुद की init विधि शुरू करेगा - जो आज्ञाकारी रूप से एक अपवाद को फेंक देगा। यह प्रारूप अधिकांश विधियों के लिए अच्छी तरह से काम करता है, लेकिन मैं इसे कभी भी ऐसा नहीं करूंगा जहां उपवर्ग इसे सुपर कार्यान्वयन कहें।
Xono

5
आप बिल्कुल उद्देश्य-सी में अमूर्त कक्षाएं बना सकते हैं, और यह काफी सामान्य है। ऐसा करने वाले कई ऐप्पल फ्रेमवर्क क्लासेस हैं। Apple के अमूर्त वर्ग विशिष्ट अपवादों (NSInvalidArgumentException) को फेंक देते हैं, जिसे अक्सर NSInvalidAbstractInvocation () कहते हैं। अमूर्त विधि को कॉल करना एक प्रोग्रामिंग त्रुटि है, यही वजह है कि यह एक अपवाद फेंकता है। अमूर्त कारखानों को आमतौर पर वर्ग समूहों के रूप में लागू किया जाता है।
क्ले

2
@quellish: जैसा कि आपने कहा: अमूर्त पद्धति को कॉल करना एक प्रोग्रामिंग त्रुटि है। रनटाइम एरर रिपोर्टिंग (NSException) पर निर्भर रहने के बजाय इसे इस तरह से माना जाना चाहिए। मुझे लगता है कि अन्य भाषाओं से आने वाले डेवलपर्स के लिए यह सबसे बड़ा मुद्दा है, जहां सार का मतलब है "ओब्ज-सी में इस प्रकार की वस्तु को तुरंत नहीं दे सकता है" इसका मतलब है "जब आप इस वर्ग को तुरंत करते हैं तो रनटाइम में चीजें गलत हो जाएंगी"।
क्रॉस_

60

बस ऊपर @Barry Wark के उत्तर पर (और iOS 4.3 के लिए अद्यतन) पर riffing और मेरे स्वयं के संदर्भ के लिए इसे छोड़:

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define methodNotImplemented() mustOverride()

तो अपने तरीकों में आप इस का उपयोग कर सकते हैं

- (void) someMethod {
     mustOverride(); // or methodNotImplemented(), same thing
}



नोट्स: निश्चित नहीं है कि एक मैक्रो लुक बनाते हुए सी फ़ंक्शन एक अच्छा विचार है या नहीं, लेकिन मैं इसे तब तक रखूंगा जब तक इसके विपरीत नहीं देखा जाता। मुझे लगता है कि इसे इस्तेमाल करने के NSInvalidArgumentExceptionबजाय (बल्कि NSInternalInconsistencyException) सही है क्योंकि रनटाइम सिस्टम को doesNotRecognizeSelectorकॉल किया जा रहा है ( NSObjectडॉक्स देखें ) के जवाब में।


1
ज़रूर, @TomA, मुझे आशा है कि यह आपको अन्य कोड के लिए विचार देता है जिसे आप स्थूल कर सकते हैं। मेरा सबसे अधिक उपयोग किया जाने वाला मैक्रो एक सिंगलटन के लिए एक सरल संदर्भ है: कोड कहता है, universe.thingलेकिन इसका विस्तार होता है [Universe universe].thing। बड़ा मज़ा, कोड के हजारों पत्रों की बचत ...
दान Rosenstark

महान। इसे थोड़ा बदल दिया, हालांकि #define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]:।
noamtm

1
@ यार: मुझे ऐसा नहीं लगता। हम __PRETTY_FUNCTION__यहां सुझाए गए DLog (...) मैक्रो के माध्यम से सभी जगह का उपयोग करते हैं: जैसा कि यहां बताया गया है: stackoverflow.com/a/969291/38557
noamtm

1
अधिक जानकारी के लिए -#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
gbk

1
यदि आप बेस क्लास को जोड़ते हैं और फिर 10 बार इस क्लास को वारिस करते हैं और किसी एक क्लास में इसे लागू करना भूल जाते हैं, तो आपको बेस क्लास के नाम के साथ मैसेज मिल जाएगा कि उन्हें विरासत में एक भी विरासत में नहीं मिला है Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category'। अगर मुझे HomeViewController - method not implementedप्रपोज़ किया गया है तो यह भी देख लें कि HomeViewController को बेस से विरासत में मिला है - यह अधिक जानकारी देंगे
gbk

42

जो समाधान मैं लेकर आया हूं वह है:

  1. अपने "सार" वर्ग में जो कुछ भी आप चाहते हैं, उसके लिए एक प्रोटोकॉल बनाएं
  2. एक बेस क्लास बनाएं (या शायद इसे एब्सट्रैक्ट कहें) जो प्रोटोकॉल को लागू करता है। उन सभी विधियों के लिए जिन्हें आप "सार" चाहते हैं, उन्हें .m फ़ाइल में लागू करें, लेकिन .h फ़ाइल के लिए नहीं।
  3. अपने बच्चे को बेस क्लास से विरासत में मिला है और प्रोटोकॉल लागू करें।

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

यह जावा की तरह रसीला नहीं है, लेकिन आपको वांछित संकलक चेतावनी मिलती है।


2
+1 यह वास्तव में समाधान है जो जावा में एक अमूर्त वर्ग के सबसे करीब आता है। मैंने स्वयं इस दृष्टिकोण का उपयोग किया है और यह बहुत अच्छा काम करता है। यहां तक ​​कि प्रोटोकॉल को आधार वर्ग (जैसे कि Apple ने किया था NSObject) के नाम से ही अनुमति दी जाती है । यदि आप प्रोटोकॉल और बेस क्लास की घोषणा को एक ही हेडर फाइल में रखते हैं तो यह एक अमूर्त वर्ग से लगभग अप्रभेद्य है।
कोडिंगफ्रीड 1

आह, लेकिन मेरे अमूर्त वर्ग एक प्रोटोकॉल का हिस्सा लागू करता है, बाकी सबक्लासेस द्वारा लागू किया जाता है।
रोजर सीएस वर्नरसन

1
आपके पास प्रोटोकॉल को लागू करने के लिए केवल बच्चे की कक्षाएं हो सकती हैं और सुपरक्लास विधियों को खाली के बजाय सभी को एक साथ छोड़ सकते हैं। फिर सुपरक्लास <मायप्रोटोकॉल> प्रकार के गुण हैं। इसके अलावा, अतिरिक्त लचीलेपन के लिए आप अपने प्रोटोकॉल में @optional के साथ अपने तरीकों को उपसर्ग कर सकते हैं।
dotToString

यह Xcode 5.0.2 में काम नहीं करता है; यह केवल "अमूर्त" वर्ग के लिए एक चेतावनी उत्पन्न करता है। "अमूर्त" वर्ग का विस्तार न करना उचित संकलक चेतावनी उत्पन्न करता है, लेकिन जाहिर है कि आप तरीकों को विरासत में नहीं देते हैं।
Topher Fangio

मैं इस समाधान को पसंद करता हूं लेकिन वास्तव में इसे पसंद नहीं करता, यह वास्तव में परियोजना में एक अच्छा कोड संरचना नहीं है। // इसे उपवर्गों के लिए आधार प्रकार के रूप में उपयोग करना चाहिए। typedef BaseClass <BaseClassProtocol> BASECLASS; यह सिर्फ एक सप्ताह का नियम है, मुझे यह पसंद नहीं है।
इटाची

35

से ओमनी समूह मेलिंग सूची :

उद्देश्य-सी में इस समय जावा की तरह अमूर्त संकलक निर्माण नहीं है।

तो आप सभी अमूर्त वर्ग को किसी अन्य सामान्य वर्ग के रूप में परिभाषित करते हैं और अमूर्त विधियों के लिए तरीकों स्टब्स को लागू करते हैं जो या तो खाली हैं या चयनकर्ता के लिए गैर-समर्थन की रिपोर्ट करते हैं। उदाहरण के लिए...

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

मैं डिफॉल्ट इनिशियलाइज़र के माध्यम से अमूर्त वर्ग के आरंभीकरण को रोकने के लिए निम्न कार्य भी करता हूं।

- (id)init
{
     [self doesNotRecognizeSelector:_cmd];
     [self release];
     return nil;
}

1
मैंने -doesNotRecognizeSelector का उपयोग करने के बारे में नहीं सोचा था: और मुझे कुछ तरीकों से यह तरीका पसंद आया। क्या किसी को संकलक को "सार" विधि के लिए चेतावनी जारी करने का कोई तरीका पता है या तो इस तरह से या एक अपवाद बढ़ाकर? यह कमाल होगा ...
क्विन टेलर

26
DoNotRecognizeSelector दृष्टिकोण Apple के सुझाए गए स्व = [सुपर init] पैटर्न को रोकता है।
dlinsin

1
@ दाविद: मुझे पूरा यकीन है कि पूरा मुद्दा जल्द से जल्द एक अपवाद को बढ़ाने के लिए है। आदर्श रूप से यह संकलन के समय पर होना चाहिए, लेकिन चूंकि ऐसा करने का कोई तरीका नहीं है, इसलिए वे एक रन समय अपवाद के लिए बस गए। यह एक जोरदार विफलता के समान है, जिसे पहली बार में उत्पादन कोड में कभी नहीं उठाया जाना चाहिए। वास्तव में, एक मुखर (गलत) वास्तव में बेहतर हो सकता है क्योंकि यह स्पष्ट है कि यह कोड कभी भी नहीं चलना चाहिए। उपयोगकर्ता इसे ठीक करने के लिए कुछ नहीं कर सकता, डेवलपर को इसे ठीक करना होगा। इस प्रकार एक अपवाद या जोर विफलता यहाँ एक अच्छा विचार की तरह लग रहा है।
सेंसफुल

2
मुझे नहीं लगता कि यह एक व्यवहार्य समाधान है क्योंकि उपवर्गों में [सुपर इनिट] के लिए पूरी तरह से वैध कॉल हो सकते हैं।
रफी खाचदौरेन

@dlinsin: यह ठीक है कि आप क्या चाहते हैं जब अमूर्त वर्ग init के माध्यम से शुरू नहीं माना जाता है। इसके उपवर्गों को संभवतः यह पता है कि कॉल करने के लिए कौन सी सुपर विधि है, लेकिन यह "नए" या "आवंटन / init" के माध्यम से लापरवाह आह्वान को रोकता है।
gnasher729

21

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

- (void)performOperation:(id<Operation>)op
{
   // do something with operation
}

ओप ऑपरेशन प्रोटोकॉल को लागू करने वाली कोई भी वस्तु हो सकती है।

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


मेरे लिए, यह उन मामलों के लिए जाने का उपयुक्त तरीका है जहां आप अमूर्त वर्ग का उपयोग करेंगे, कम से कम जब इसका मतलब वास्तव में "इंटरफ़ेस" (जैसे सी ++) होता है। क्या इस दृष्टिकोण में कोई छिपी हुई गिरावट है?
फेबेलिंग

1
@febeling, अमूर्त वर्ग - कम से कम जावा में - केवल इंटरफेस नहीं हैं। वे कुछ (या अधिकांश) व्यवहार को भी परिभाषित करते हैं। यह दृष्टिकोण कुछ मामलों में अच्छा हो सकता है, हालांकि।
दान रोसेनस्टार्क

मुझे कुछ कार्यक्षमता को लागू करने के लिए एक आधार वर्ग की आवश्यकता है जो मेरे उपवर्ग सभी शेयर (दोहराव को हटाते हुए) को लागू करते हैं, लेकिन मुझे अन्य तरीकों के लिए एक प्रोटोकॉल की भी आवश्यकता है जो बेसकलैस (सार भाग) को संभालना नहीं चाहिए। इसलिए मुझे दोनों का उपयोग करने की आवश्यकता है, और यह कि जहां यह सुनिश्चित करने के लिए मुश्किल हो सकता है कि आपके उपवर्ग अपने आप को ठीक से लागू कर रहे हैं।
लाइटनिंगस्ट्राइक

19

यह धागा एक प्रकार का पुराना है, और जो मैं साझा करना चाहता हूं उसका अधिकांश हिस्सा पहले से ही यहां है।

हालांकि, मेरी पसंदीदा विधि का उल्लेख नहीं किया गया है, और AFAIK वर्तमान क्लैंग में कोई मूल समर्थन नहीं है, इसलिए यहां मैं जाता हूं ...

सबसे पहले, और सबसे महत्वपूर्ण (जैसा कि अन्य ने पहले ही बताया है) ऑब्जेक्टिव-सी में अमूर्त वर्ग कुछ बहुत ही असामान्य हैं - हम आमतौर पर इसके बजाय रचना (कभी-कभी प्रतिनिधिमंडल के माध्यम से) का उपयोग करते हैं। शायद यही कारण है कि इस तरह की सुविधा पहले से ही भाषा / संकलक में मौजूद नहीं है - @dynamicगुणों के अलावा , जिसे IIRC को ओब्जेक्ट 2.0 में कोरडाटा के साथ जोड़ा गया है।

लेकिन यह देखते हुए कि (आपकी स्थिति के सावधानीपूर्वक मूल्यांकन के बाद!) आप इस निष्कर्ष पर पहुंचे हैं कि प्रतिनिधिमंडल (या सामान्य रूप से रचना) आपकी समस्या को हल करने के लिए अच्छी तरह से अनुकूल नहीं है, यहाँ मैं यह कैसे करूँ:

  1. बेस क्लास में हर अमूर्त पद्धति को लागू करें।
  2. इसे लागू करें [self doesNotRecognizeSelector:_cmd];...
  3. … इसके बाद __builtin_unreachable();आपको गैर-शून्य तरीकों के लिए मिलने वाली चेतावनी को चुप कराने के लिए आपको बताएंगे कि “नियंत्रण बिना रिटर्न के गैर-शून्य फ़ंक्शन के अंत तक पहुंच गया”।
  4. या तो चरण 2 और 3 को एक मैक्रो में संयोजित करें, या किसी श्रेणी में -[NSObject doesNotRecognizeSelector:]उपयोग किए बिना एनोटेट करें ताकि उस पद्धति के मूल कार्यान्वयन को प्रतिस्थापित न किया जा सके और अपनी परियोजना के पीसीएच में उस श्रेणी के हेडर को शामिल कर सकें।__attribute__((__noreturn__))

मैं व्यक्तिगत रूप से मैक्रो संस्करण को पसंद करता हूं जो मुझे बॉयलरप्लेट को यथासंभव कम करने की अनुमति देता है।

यह रहा:

// Definition:
#define D12_ABSTRACT_METHOD {\
 [self doesNotRecognizeSelector:_cmd]; \
 __builtin_unreachable(); \
}

// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString

#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD

#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
    if (aRange.location + aRange.length >= [self length])
        [NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];

    unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
    [self getCharacters:buffer range:aRange];

    return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…

@end

जैसा कि आप देख सकते हैं, मैक्रो सार विधियों के पूर्ण कार्यान्वयन को प्रदान करता है, बॉयलरप्लेट की आवश्यक मात्रा को पूर्णतम तक कम कर देता है।

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

क्यों मैं इस विधि का चयन

  1. यह कुशलता से काम किया है, और कुछ हद तक सुविधाजनक है।
  2. इसे समझना काफी आसान है। (ठीक है, जो __builtin_unreachable()लोगों को आश्चर्यचकित कर सकता है, लेकिन यह समझना काफी आसान है, भी)
  3. इसे अन्य कंपाइलर चेतावनियों या त्रुटियों को उत्पन्न किए बिना रिलीज़ बिल्डिंग्स में छीन लिया जा सकता है - एक दृष्टिकोण के विपरीत जो कि एक दावे के मैक्रोज़ पर आधारित है।

मुझे लगता है कि अंतिम बिंदु कुछ स्पष्टीकरण की जरूरत है:

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

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


यह मेरा नया पसंदीदा समाधान है - __builtin_unreachable();मणि इस काम को पूरी तरह से करता है। मैक्रो इसे स्व-दस्तावेजीकरण बनाता है और व्यवहार से मेल खाता है यदि आप किसी ऑब्जेक्ट पर एक लापता विधि कहते हैं।
एलेक्स एमडीसी

12

का उपयोग करना @propertyऔर @dynamicभी काम कर सकता है। यदि आप एक गतिशील संपत्ति की घोषणा करते हैं और मिलान विधि लागू नहीं करते हैं, तो सब कुछ अभी भी चेतावनी के बिना संकलन करेगा, और unrecognized selectorयदि आप इसे एक्सेस करने का प्रयास करते हैं, तो आपको रनटाइम पर एक त्रुटि मिलेगी। यह अनिवार्य रूप से कॉलिंग के समान है [self doesNotRecognizeSelector:_cmd], लेकिन बहुत कम टाइपिंग के साथ।


7

Xcode में (क्लैंग आदि का उपयोग करके) मैं __attribute__((unavailable(...)))एब्स्ट्रैक्ट क्लासेस को टैग करने के लिए उपयोग करना पसंद करता हूं ताकि आपको कोशिश करने और उपयोग करने पर कोई त्रुटि / चेतावनी मिले।

यह गलती से विधि के उपयोग से कुछ सुरक्षा प्रदान करता है।

उदाहरण

बेस क्लास @interfaceटैग में "अमूर्त" तरीके:

- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));

इस एक कदम को आगे बढ़ाते हुए, मैं एक मैक्रो बनाता हूं:

#define UnavailableMacro(msg) __attribute__((unavailable(msg)))

इससे आप ऐसा कर सकते हैं:

- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");

जैसा कि मैंने कहा, यह वास्तविक संकलक संरक्षण नहीं है, लेकिन यह उतना ही अच्छा है जितना कि एक ऐसी भाषा में जाना है जो अमूर्त तरीकों का समर्थन नहीं करती है।


1
मैं इसे प्राप्त नहीं कर सका। मैं अपने आधार वर्ग पर अपने सुझाव लागू किया -init विधि और अब Xcode मुझे विरासत में मिला वर्ग का एक उदाहरण है और एक संकलन समय त्रुटि तब होती है बनाने की अनुमति नहीं है उपलब्ध नहीं हैं ... । क्या आप और अधिक समझाएंगे?
अनॉनिम

सुनिश्चित करें कि आपके पास -initअपने उपवर्ग में एक विधि है।
रिचर्ड स्टेलिंग

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

7

पहले से दिए गए उत्तरों के तहत टिप्पणियों में प्रश्न का उत्तर चारों ओर बिखरा हुआ है। इसलिए, मैं यहां संक्षेप और सरल कर रहा हूं।

विकल्प 1: प्रोटोकॉल

यदि आप कोई कार्यान्वयन उपयोग 'प्रोटोकॉल' के साथ एक सार वर्ग बनाना चाहते हैं। प्रोटोकॉल प्राप्त करने वाले वर्ग प्रोटोकॉल में विधियों को लागू करने के लिए बाध्य हैं।

@protocol ProtocolName
// list of methods and properties
@end

Option2: टेम्पलेट विधि पैटर्न

यदि आप "टेम्पलेट मेथड पैटर्न" जैसे आंशिक कार्यान्वयन के साथ एक अमूर्त वर्ग बनाना चाहते हैं तो यह समाधान है। उद्देश्य-सी - टेम्पलेट विधियों पैटर्न?


6

एक और विकल्प

बस सार वर्ग और जोर या अपवाद में कक्षा की जांच करें, जो भी आप कल्पना करते हैं।

@implementation Orange
- (instancetype)init
{
    self = [super init];
    NSAssert([self class] != [Orange class], @"This is an abstract class");
    if (self) {
    }
    return self;
}
@end

यह ओवरराइड करने की आवश्यकता को दूर करता है init


क्या यह सिर्फ शून्य वापस करने के लिए कुशल होगा? या इससे अपवाद / अन्य त्रुटि का कारण होगा?
user2277872

खैर यह सिर्फ प्रोग्रामर को सीधे वर्ग का उपयोग करने के लिए पकड़ना है, जो मुझे लगता है कि एक जोर के अच्छे उपयोग के लिए बनाता है।
बीते

5

(संबंधित सुझाव के अधिक)

मैं चाहता था कि प्रोग्रामर को "बच्चे से कॉल न करें" और पूरी तरह से ओवरराइड करने की अनुमति देने का एक तरीका है (मेरे मामले में अभी भी माता-पिता की ओर से कुछ डिफ़ॉल्ट कार्यक्षमता प्रदान की जाती है जब विस्तारित नहीं होती है):

typedef void override_void;
typedef id override_id;

@implementation myBaseClass

// some limited default behavior (undesired by subclasses)
- (override_void) doSomething;
- (override_id) makeSomeObject;

// some internally required default behavior
- (void) doesSomethingImportant;

@end

लाभ यह है कि प्रोग्रामर घोषणा में "ओवरराइड" को देखेगा और जानता है कि उन्हें कॉल नहीं करना चाहिए [super ..]

दी गई, इसके लिए व्यक्तिगत रिटर्न प्रकारों को परिभाषित करना बदसूरत है, लेकिन यह एक अच्छा पर्याप्त दृश्य संकेत के रूप में कार्य करता है और आप आसानी से उपवर्ग परिभाषा में "ओवरराइड" भाग का उपयोग नहीं कर सकते हैं।

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

संकलक संकेत में इस तरह से निर्मित होना अच्छा होगा, यहां तक ​​कि संकेत के लिए जब यह टिप्पणी / प्रलेखन या ... के माध्यम से खोदने के बजाय सुपर के कार्यान्वयन को पूर्व / पोस्ट कॉल करने के लिए सबसे अच्छा है।

संकेत का उदाहरण


4

यदि आप अन्य भाषाओं में सार तात्कालिक उल्लंघनों को पकड़ने वाले कंपाइलर के लिए उपयोग किए जाते हैं, तो उद्देश्य-सी व्यवहार निराशाजनक है।

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

यहाँ एक पैटर्न है जिसे हम इस प्रकार की स्थैतिक जाँच प्राप्त करने के लिए उपयोग कर रहे हैं कि शुरुआती तकनीकों को छिपाने के लिए कुछ तकनीकों का उपयोग किया जाए:

//
//  Base.h
#define UNAVAILABLE __attribute__((unavailable("Default initializer not available.")));

@protocol MyProtocol <NSObject>
-(void) dependentFunction;
@end

@interface Base : NSObject {
    @protected
    __weak id<MyProtocol> _protocolHelper; // Weak to prevent retain cycles!
}

- (instancetype) init UNAVAILABLE; // Prevent the user from calling this
- (void) doStuffUsingDependentFunction;
@end

//
//  Base.m
#import "Base.h"

// We know that Base has a hidden initializer method.
// Declare it here for readability.
@interface Base (Private)
- (instancetype)initFromDerived;
@end

@implementation Base
- (instancetype)initFromDerived {
    // It is unlikely that this becomes incorrect, but assert
    // just in case.
    NSAssert(![self isMemberOfClass:[Base class]],
             @"To be called only from derived classes!");
    self = [super init];
    return self;
}

- (void) doStuffUsingDependentFunction {
    [_protocolHelper dependentFunction]; // Use it
}
@end

//
//  Derived.h
#import "Base.h"

@interface Derived : Base
-(instancetype) initDerived; // We cannot use init here :(
@end

//
//  Derived.m
#import "Derived.h"

// We know that Base has a hidden initializer method.
// Declare it here.
@interface Base (Private)
- (instancetype) initFromDerived;
@end

// Privately inherit protocol
@interface Derived () <MyProtocol>
@end

@implementation Derived
-(instancetype) initDerived {
    self= [super initFromDerived];
    if (self) {
        self->_protocolHelper= self;
    }
    return self;
}

// Implement the missing function
-(void)dependentFunction {
}
@end

3

संभवतः इस प्रकार की परिस्थितियाँ केवल विकास के समय ही होनी चाहिए, इसलिए यह काम कर सकती है:

- (id)myMethodWithVar:(id)var {
   NSAssert(NO, @"You most override myMethodWithVar:");
   return nil;
}

3

आप @ यार द्वारा प्रस्तावित विधि का उपयोग कर सकते हैं (कुछ संशोधन के साथ):

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()

यहां आपको एक संदेश मिलेगा:

<Date> ProjectName[7921:1967092] <Class where method not implemented> - method not implemented
<Date> ProjectName[7921:1967092] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[<Base class (if inherited or same if not> <Method name>] must be overridden in a subclass/category'

या दावा:

NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");

इस मामले में आपको मिलेगा:

<Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53

इसके अलावा, आप प्रोटोकॉल और अन्य समाधानों का उपयोग कर सकते हैं - लेकिन यह सबसे सरल में से एक है।


2

कोको सार नामक कुछ भी प्रदान नहीं करता है। हम एक क्लास एब्सट्रैक्ट बना सकते हैं जो केवल रनटाइम पर चेक किया जाता है, और संकलन समय पर यह चेक नहीं किया जाता है।


1

मैं आमतौर पर एक वर्ग में init पद्धति को अक्षम करता हूं जिसे मैं सार करना चाहता हूं:

- (instancetype)__unavailable init; // This is an abstract class.

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

ऑब्जेक्टिव-सी में अमूर्त वर्ग घोषित करने का कोई अंतर्निहित तरीका नहीं है।


लेकिन मुझे त्रुटि हो रही है जब मैं उस सार वर्ग के व्युत्पन्न वर्ग से [सुपर इनिट] कहता हूं। कैसे हल करें?
साहिल दोशी

@SahilDoshi मुझे लगता है कि इस विधि का उपयोग करने के नकारात्मक पक्ष है। मैं इसका उपयोग तब करता हूं जब मैं किसी वर्ग को तात्कालिक होने देना नहीं चाहता, और उस वर्ग को कुछ भी विरासत में नहीं मिलता है।
माहूकोव

हाँ, मैं समझ गया था कि लेकिन आपका समाधान सिंगलटन क्लास की तरह कुछ बनाने में मदद करेगा जहाँ हम नहीं चाहते कि कोई भी इनिट कहे। अमूर्त वर्ग के मामले में मेरे ज्ञान के अनुसार कुछ वर्ग इसे विरासत में लेने जा रहे हैं। वरना अमूर्त वर्ग का उपयोग।
साहिल दोषी

0

@ DotToString की टिप्पणी को लागू करके @redfood ने जो थोड़ा सा सुझाव दिया था, उसे बदलना, आपके पास वास्तव में Instagram के IGListKit द्वारा अपनाया गया समाधान है ।

  1. सभी तरीकों के लिए एक प्रोटोकॉल बनाएं जो आधार (अमूर्त) वर्ग में परिभाषित करने के लिए कोई मतलब नहीं बनाते हैं अर्थात उन्हें बच्चों में विशिष्ट कार्यान्वयन की आवश्यकता होती है।
  2. एक आधार (सार) वर्ग बनाएं जो इस प्रोटोकॉल को लागू नहीं करता है। आप इस वर्ग को किसी भी अन्य तरीकों से जोड़ सकते हैं जो सामान्य कार्यान्वयन के लिए समझ में आता है।
  3. अपनी परियोजना में हर जगह, अगर किसी बच्चे को AbstractClassकिसी विधि द्वारा इनपुट या आउटपुट होना चाहिए , तो AbstractClass<Protocol>इसके बजाय इसे टाइप करें ।

क्योंकि AbstractClassलागू नहीं होता है Protocol, AbstractClass<Protocol>उदाहरण के लिए एक ही रास्ता उपवर्ग द्वारा है। के रूप में AbstractClassअकेले परियोजना में कहीं भी नहीं किया जा सकता है, यह सार हो जाता है।

बेशक, यह अप्रभावित डेवलपर्स को केवल उल्लेख करने वाले नए तरीकों को जोड़ने से नहीं रोकता है AbstractClass, जो कि (अब और नहीं) सार वर्ग के एक उदाहरण की अनुमति देगा।

वास्तविक दुनिया का उदाहरण: IGListKit में एक बेस क्लास है IGListSectionControllerजो प्रोटोकॉल को लागू नहीं करता है IGListSectionType, हालांकि हर विधि जिसमें उस वर्ग के उदाहरण की आवश्यकता होती है, वास्तव में प्रकार के लिए पूछता है IGListSectionController<IGListSectionType>। इसलिए IGListSectionControllerउनके ढांचे में उपयोगी किसी भी प्रकार की वस्तु का उपयोग करने का कोई तरीका नहीं है ।


0

वास्तव में, ऑब्जेक्टिव-सी में अमूर्त कक्षाएं नहीं हैं, लेकिन आप उसी प्रभाव को प्राप्त करने के लिए प्रोटोकॉल का उपयोग कर सकते हैं । यहाँ नमूना है:

CustomProtocol.h

#import <Foundation/Foundation.h>

@protocol CustomProtocol <NSObject>
@required
- (void)methodA;
@optional
- (void)methodB;
@end

TestProtocol.h

#import <Foundation/Foundation.h>
#import "CustomProtocol.h"

@interface TestProtocol : NSObject <CustomProtocol>

@end

TestProtocol.m

#import "TestProtocol.h"

@implementation TestProtocol

- (void)methodA
{
  NSLog(@"methodA...");
}

- (void)methodB
{
  NSLog(@"methodB...");
}
@end

0

एक अमूर्त वर्ग बनाने का एक सरल उदाहरण

// Declare a protocol
@protocol AbcProtocol <NSObject>

-(void)fnOne;
-(void)fnTwo;

@optional

-(void)fnThree;

@end

// Abstract class
@interface AbstractAbc : NSObject<AbcProtocol>

@end

@implementation AbstractAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

-(void)fnOne{
// Code
}

-(void)fnTwo{
// Code
}

@end

// Implementation class
@interface ImpAbc : AbstractAbc

@end

@implementation ImpAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

// You may override it    
-(void)fnOne{
// Code
}
// You may override it
-(void)fnTwo{
// Code
}

-(void)fnThree{
// Code
}

@end

-3

क्या आप सिर्फ एक प्रतिनिधि नहीं बना सकते?

एक प्रतिनिधि इस अर्थ में एक सार आधार वर्ग की तरह है कि आप कहते हैं कि किन कार्यों को परिभाषित करने की आवश्यकता है, लेकिन आप वास्तव में उन्हें परिभाषित नहीं करते हैं।

फिर जब भी आप अपने प्रतिनिधि (यानी अमूर्त वर्ग) को लागू करते हैं, तो आपको इस बात के संकलक द्वारा चेतावनी दी जाती है कि आपको किन वैकल्पिक और अनिवार्य कार्यों के लिए परिभाषित करना है।

यह मुझे एक सार आधार वर्ग की तरह लगता है।

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