स्विफ्ट-विशिष्ट जानकारी के दो बिल्कुल महत्वपूर्ण टुकड़े हैं जो मौजूदा उत्तरों से गायब हैं जो मुझे लगता है कि इसे पूरी तरह से स्पष्ट करने में मदद करते हैं।
- यदि कोई प्रोटोकॉल एक इनिशियलाइज़र को एक आवश्यक विधि के रूप में निर्दिष्ट करता है, तो उस इनिलाइज़र को स्विफ्ट के
required
कीवर्ड का उपयोग करके चिह्नित किया जाना चाहिए ।
- स्विफ्ट में
init
विधियों के संबंध में विरासत नियमों का एक विशेष सेट है ।
Tl; डॉ यह है:
यदि आप कोई भी इनिलाइज़र लागू करते हैं, तो आप अब किसी भी सुपरक्लास के निर्दिष्ट इनिशियलाइज़र को विरासत में नहीं पा सकते हैं।
केवल इनिशियलाइज़र, यदि कोई हो, जिसे आप इनहेरिट करेंगे, सुपर क्लास सुविधा इनिशियलाइज़र हैं जो एक निर्दिष्ट इनिशियलाइज़र को इंगित करते हैं जिसे आपने ओवरराइड किया था।
तो ... लंबे संस्करण के लिए तैयार हैं?
स्विफ्ट में init
विधियों के संबंध में विरासत नियमों का एक विशेष सेट है ।
मुझे पता है कि यह मेरे द्वारा बनाए गए दो बिंदुओं में से दूसरा था, लेकिन हम पहले बिंदु को नहीं समझ सकते, या required
जब तक हम इस बिंदु को नहीं समझते तब तक कीवर्ड क्यों मौजूद है। एक बार जब हम इस बिंदु को समझ जाते हैं, तो दूसरा स्पष्ट हो जाता है।
इस उत्तर के इस भाग में मेरे द्वारा कवर की गई सभी जानकारी Apple के प्रलेखन से मिली है ।
Apple डॉक्स से:
ऑब्जेक्टिव-सी में उप-वर्ग के विपरीत, स्विफ्ट उप-वर्ग अपने सुपरक्लास इनिशियलाइज़र को डिफ़ॉल्ट रूप से विरासत में नहीं लेते हैं। स्विफ्ट का दृष्टिकोण एक ऐसी स्थिति को रोकता है जिसमें सुपरक्लास से एक साधारण इनिशियलाइज़र को अधिक विशिष्ट उपवर्ग द्वारा विरासत में मिला है और इसका उपयोग उप-वर्ग का एक नया उदाहरण बनाने के लिए किया जाता है जो पूरी तरह से या सही ढंग से आरंभीकृत नहीं है।
जोर मेरा।
तो, Apple डॉक्स से सीधे वहीं, हम देखते हैं कि स्विफ्ट उपवर्ग हमेशा (और आमतौर पर नहीं) उनके सुपरक्लास के init
तरीकों को विरासत में नहीं मिलेगा ।
तो, वे अपने सुपरक्लास से कब विरासत में लेते हैं?
दो नियम हैं जो परिभाषित करते हैं कि एक उपवर्ग init
अपने माता-पिता से विधियां प्राप्त करता है। Apple डॉक्स से:
नियम 1
यदि आपका उपवर्ग किसी निर्दिष्ट आरंभीकृत को परिभाषित नहीं करता है, तो यह स्वचालित रूप से अपने सभी सुपरक्लास नामित आरंभिकों को विरासत में देता है।
नियम २
यदि आपका उपवर्ग अपने सभी सुपरक्लास नामित इनिशियलाइज़र के कार्यान्वयन को प्रदान करता है - या तो उन्हें नियम 1 के अनुसार विरासत में मिलता है, या अपनी परिभाषा के भाग के रूप में एक कस्टम कार्यान्वयन प्रदान करता है - तो यह स्वचालित रूप से सभी सुपरक्लास सुविधा इनिशियलाइज़र को विरासत में मिलती है।
नियम 2 क्योंकि इस बातचीत के लिए विशेष रूप से प्रासंगिक नहीं है SKSpriteNode
के init(coder: NSCoder)
लिए एक सुविधा विधि होने की संभावना नहीं है।
इसलिए, आपकी InfoBar
कक्षा को required
प्रारंभिक बिंदु विरासत में मिला था , जब तक कि आपने जो बिंदु नहीं जोड़ा था init(team: Team, size: CGSize)
।
तुम थे इस प्रदान नहीं किया है करने के लिए, तो init
विधि और इसके बजाय अपने किए गए InfoBar
's को जोड़ा गया गुण वैकल्पिक या मूलभूत मूल्यों के साथ उन्हें प्रदान की है, तो आप अब भी विरासत में दिया है चाहता हूँ SKSpriteNode
' s init(coder: NSCoder)
। हालाँकि, जब हमने अपने कस्टम इनिशियलाइज़र को जोड़ा, तो हमने अपने सुपरक्लास के नामित इनिशियलाइज़र (और सुविधा इनिशियलाइज़र जो हमने लागू किया इनिशियलाइज़र्स को इंगित नहीं किया था) को विरासत में देना बंद कर दिया ।
इसलिए, एक सरल उदाहरण के रूप में, मैं इसे प्रस्तुत करता हूं:
class Foo {
var foo: String
init(foo: String) {
self.foo = foo
}
}
class Bar: Foo {
var bar: String
init(foo: String, bar: String) {
self.bar = bar
super.init(foo: foo)
}
}
let x = Bar(foo: "Foo")
जो निम्नलिखित त्रुटि प्रस्तुत करता है:
कॉल में पैरामीटर 'बार' के लिए लापता तर्क।
यदि यह उद्देश्य-सी होते हैं, तो इसे विरासत में कोई समस्या नहीं होगी। यदि हमने ऑब्जेक्टिव-सी के Bar
साथ इनिशियलाइज़ किया initWithFoo:
, तो self.bar
संपत्ति बस होगी nil
। यह शायद महान नहीं है, लेकिन यह एक पूरी तरह से है मान्य के लिए वस्तु में होने की राज्य। यह नहीं के लिए स्विफ्ट वस्तु में होने के लिए एक पूरी तरह से वैध राज्य। self.bar
एक वैकल्पिक नहीं है और नहीं किया जा सकता nil
।
फिर से, हमें इनिशियलाइज़र्स का एकमात्र तरीका विरासत में मिला है, जो हमें स्वयं प्रदान नहीं करता है। इसलिए यदि हम हटाने Bar
की कोशिश करते हैं init(foo: String, bar: String)
, जैसे कि:
class Bar: Foo {
var bar: String
}
अब हम विरासत में वापस आ रहे हैं (प्रकार), लेकिन यह संकलित नहीं करेगा ... और त्रुटि संदेश ठीक यही बताता है कि हमें अधिवास init
विधि क्यों नहीं मिली :
समस्या: कक्षा 'बार' का कोई आरंभक नहीं है
फिक्स-इट: स्टैरिअलाइज़र्स के बिना संग्रहीत प्रॉपर्टी 'बार' संश्लेषित इनिशियलाइज़र को रोकता है
यदि हमने अपने उपवर्ग में संग्रहीत संपत्तियाँ जोड़ी हैं, तो सुपरक्लास इनिशियलाइज़र के साथ हमारे उपवर्ग का एक वैध उदाहरण बनाने के लिए कोई संभव स्विफ्ट तरीका नहीं है जो संभवतः हमारे उपवर्ग के संग्रहीत गुणों के बारे में नहीं जान सकता है।
ठीक है, ठीक है, मुझे क्यों लागू init(coder: NSCoder)
करना है? क्यों है required
?
स्विफ्ट के init
तरीके विरासत नियमों के एक विशेष सेट द्वारा खेल सकते हैं, लेकिन प्रोटोकॉल अनुरूपता अभी भी श्रृंखला के नीचे विरासत में मिली है। यदि कोई मूल वर्ग किसी प्रोटोकॉल के अनुरूप है, तो उसके उपवर्गों को उस प्रोटोकॉल के अनुरूप होना चाहिए।
आमतौर पर, यह एक समस्या नहीं है, क्योंकि अधिकांश प्रोटोकॉल के लिए केवल उन विधियों की आवश्यकता होती है, जो स्विफ्ट में विशेष विरासत नियमों द्वारा नहीं खेलती हैं, इसलिए यदि आप एक वर्ग से विरासत में प्राप्त कर रहे हैं जो एक प्रोटोकॉल के अनुरूप है, तो आप सभी को भी विरासत में मिला रहे हैं तरीके या गुण जो वर्ग को प्रोटोकॉल अनुरूपता को पूरा करने की अनुमति देते हैं।
हालांकि, याद रखें, स्विफ्ट के init
तरीके नियमों के एक विशेष सेट द्वारा खेलते हैं और हमेशा विरासत में नहीं मिलते हैं। इस वजह से, एक वर्ग जो एक प्रोटोकॉल के अनुरूप होता है जिसे विशेष init
विधियों (जैसे NSCoding
) की आवश्यकता होती है, वर्ग को उन init
तरीकों को चिह्नित करना होता है required
।
इस उदाहरण पर विचार करें:
protocol InitProtocol {
init(foo: Int)
}
class ConformingClass: InitProtocol {
var foo: Int
init(foo: Int) {
self.foo = foo
}
}
यह संकलन नहीं है। यह निम्नलिखित चेतावनी उत्पन्न करता है:
समस्या: प्रारंभिक आवश्यकता 'init (foo :)' को केवल गैर-अंतिम वर्ग 'कॉनफ़ॉर्मिंगक्लास' में 'आवश्यक' इनिशियलाइज़र द्वारा संतुष्ट किया जा सकता है
फिक्स-इट: आवश्यक सम्मिलित करें
यह मुझे init(foo: Int)
इनिशियलाइज़र को आवश्यक बनाने के लिए चाहता है । मैं वर्ग को खुश करके भी खुश कर सकता था final
(मतलब वर्ग को विरासत में नहीं मिला जा सकता है)।
तो, क्या होगा अगर मैं उपवर्ग? इस बिंदु से, अगर मैं उपवर्ग करता हूं, तो मैं ठीक हूं। यदि मैं किसी भी इनिशियलाइज़र को जोड़ता हूं, तो मैं अचानक विरासत में नहीं हूं init(foo:)
। यह समस्याग्रस्त है क्योंकि अब मैं इसके अनुरूप नहीं हूं InitProtocol
। मैं उस वर्ग से उपवर्ग नहीं कर सकता, जो किसी प्रोटोकॉल के अनुरूप है और फिर अचानक तय करता है कि मैं अब उस प्रोटोकॉल के अनुरूप नहीं होना चाहता। मुझे प्रोटोकॉल अनुरूपता विरासत में मिली है, लेकिन स्विफ्ट जिस तरह से init
विधि वंशानुक्रम के साथ काम करता है , मुझे उस प्रोटोकॉल के अनुरूप होने के लिए आवश्यक हिस्सा विरासत में नहीं मिला है और मुझे इसे लागू करना चाहिए।
ठीक है, यह सब समझ में आता है। लेकिन मुझे अधिक उपयोगी त्रुटि संदेश क्यों नहीं मिल सकता है?
तर्क से, त्रुटि संदेश अधिक स्पष्ट या बेहतर हो सकता है यदि यह निर्दिष्ट करता है कि आपकी कक्षा विरासत में प्राप्त NSCoding
प्रोटोकॉल के अनुरूप नहीं थी और इसे ठीक करने के लिए आपको इसे लागू करने की आवश्यकता है init(coder: NSCoder)
। ज़रूर।
लेकिन Xcode बस उस संदेश को उत्पन्न नहीं कर सकता है क्योंकि यह वास्तव में हमेशा आवश्यक समस्या को लागू करने या विरासत में नहीं लेने के साथ वास्तविक समस्या होगी। प्रोटोकॉल के अनुरूप init
तरीके बनाने के लिए कम से कम एक और कारण है required
, और वह है कारखाने के तरीके।
यदि मैं एक उचित कारखाना विधि लिखना चाहता हूं, तो मुझे रिटर्न प्रकार Self
(ऑब्जेक्ट-सी के स्विफ्ट के समकक्ष instanceType
) को निर्दिष्ट करने की आवश्यकता है । लेकिन ऐसा करने के लिए, मुझे वास्तव में एक required
शुरुआती विधि का उपयोग करने की आवश्यकता है ।
class Box {
var size: CGSize
init(size: CGSize) {
self.size = size
}
class func factory() -> Self {
return self.init(size: CGSizeZero)
}
}
यह त्रुटि उत्पन्न करता है:
एक मेटाटाइप मान के साथ वर्ग प्रकार 'सेल्फ' के ऑब्जेक्ट का निर्माण एक 'आवश्यक' इनिशियलाइज़र का उपयोग करना चाहिए
यह मूल रूप से एक ही समस्या है। यदि हम उपवर्ग करते हैं Box
, तो हमारे उपवर्ग वर्ग विधि को विरासत में देंगे factory
। तो हम कॉल कर सकते थे SubclassedBox.factory()
। हालांकि, बिना required
पर कीवर्ड init(size:)
विधि, Box
के उपवर्गों के वारिस की गारंटी नहीं दी self.init(size:)
है कि factory
बुला रहा है।
इसलिए हमें इस विधि को बनाना चाहिए required
अगर हम इस तरह का कारखाना तरीका चाहते हैं, और इसका मतलब है कि यदि हमारी कक्षा इस तरह की विधि लागू करती है, तो हमारे पास एक required
प्रारंभिक विधि होगी और हम आपके द्वारा यहां चलाई गई ठीक उसी समस्याओं में भाग लेंगे NSCoding
प्रोटोकॉल के साथ ।
अंततः, यह सभी मूल समझ को उबालता है कि स्विफ्ट के इनिशियलाइज़र इनहेरिटेंस नियमों के थोड़े अलग सेट द्वारा खेलते हैं जिसका अर्थ है कि आपको अपने सुपरक्लास से इनिशियलाइज़र्स को प्राप्त करने की गारंटी नहीं है। ऐसा इसलिए होता है क्योंकि सुपरक्लास इनिशियलाइज़र आपके नए संग्रहित गुणों के बारे में नहीं जान सकते हैं और वे आपकी वस्तु को एक मान्य स्थिति में नहीं ला सकते हैं। लेकिन, विभिन्न कारणों से, एक सुपरक्लास एक इनिशियलाइज़र को चिह्नित कर सकता है required
। जब यह होता है, तो हम या तो बहुत विशिष्ट परिदृश्यों में से एक को नियोजित कर सकते हैं जिसके द्वारा हम वास्तव में required
विधि को प्राप्त करते हैं , या हमें इसे स्वयं लागू करना चाहिए।
हालांकि यहां मुख्य बिंदु यह है कि यदि हम आपको यहां दिखाई देने वाली त्रुटि प्राप्त कर रहे हैं, तो इसका मतलब है कि आपकी कक्षा वास्तव में विधि को लागू नहीं कर रही है।
जैसा कि शायद एक अंतिम उदाहरण इस तथ्य में ड्रिल करने के लिए है कि स्विफ्ट उपवर्ग हमेशा अपने माता-पिता के init
तरीकों को विरासत में नहीं लेते हैं (जो मुझे लगता है कि इस समस्या को पूरी तरह से समझने के लिए बिल्कुल केंद्रीय है), इस उदाहरण पर विचार करें:
class Foo {
init(a: Int, b: Int, c: Int) {
// do nothing
}
}
class Bar: Foo {
init(string: String) {
super.init(a: 0, b: 1, c: 2)
// do more nothing
}
}
let f = Foo(a: 0, b: 1, c: 2)
let b = Bar(a: 0, b: 1, c: 2)
यह संकलन करने में विफल रहता है।
त्रुटि संदेश जो देता है वह थोड़ा भ्रामक है:
कॉल में अतिरिक्त तर्क 'बी'
लेकिन बिंदु, है Bar
में से किसी के वारिस नहीं है Foo
के init
तरीकों, क्योंकि यह दो विशेष मामलों में से किसी इनहेरिट के लिए पूरी नहीं की init
अपनी मूल वर्ग से तरीकों।
यदि यह ऑब्जेक्टिव-सी थे, तो हमें init
कोई समस्या नहीं होगी, क्योंकि ऑब्जेक्टिव-सी ऑब्जेक्ट्स की प्रॉपर्टीज़ को इनिशियलाइज़ नहीं करने से पूरी तरह खुश है (हालाँकि एक डेवलपर के रूप में, आपको इससे खुश नहीं होना चाहिए)। स्विफ्ट में, यह बस नहीं चलेगा। आपके पास एक अमान्य स्थिति नहीं हो सकती है, और सुपरक्लास इनिशियलाइज़र को विरासत में प्राप्त करने से केवल अमान्य ऑब्जेक्ट स्थिति हो सकती है।
init(collection:MPMediaItemCollection)
। आपको एक वास्तविक मीडिया आइटम संग्रह की आपूर्ति करनी चाहिए; यह इस वर्ग की बात है। यह वर्ग केवल एक के बिना त्वरित नहीं किया जा सकता है। यह संग्रह का विश्लेषण करने और एक दर्जन उदाहरण चर शुरू करने जा रहा है। यही कारण है कि यह केवल और केवल शुरुआती इनिशियलाइज़र है! इस प्रकार,init(coder:)
यहाँ आपूर्ति करने के लिए कोई सार्थक (या यहां तक कि अर्थहीन) MPMediaItemCollection नहीं है; केवलfatalError
दृष्टिकोण ही सही है।