शुरुआती सुपरवाइज़र अपने सुपरक्लास पर सुविधा इनिशियलाइज़र को क्यों नहीं बुला सकते?


88

दो वर्गों पर विचार करें:

class A {
    var x: Int

    init(x: Int) {
        self.x = x
    }

    convenience init() {
        self.init(x: 0)
    }
}

class B: A {
    init() {
        super.init() // Error: Must call a designated initializer of the superclass 'A'
    }
}

मैं यह नहीं देखता कि इसकी अनुमति क्यों नहीं है। अंत में, प्रत्येक वर्ग के नामित प्रारंभकर्ता कोई भी मान वे जरूरत के साथ कहा जाता है, तो क्यों मैं में अपने आप को दोहराने की आवश्यकता है B'एस initके लिए एक डिफ़ॉल्ट मान निर्दिष्ट करके xफिर से, जब सुविधा initमें Aठीक क्या करेंगे?


2
मैंने उत्तर की तलाश की, लेकिन मुझे ऐसा कोई नहीं मिला जो मुझे संतुष्ट करे। यह शायद कुछ कार्यान्वयन का कारण है। हो सकता है कि सुविधा इनिशियलाइज़र की खोज करने की तुलना में किसी अन्य वर्ग में नामित शुरुआती के लिए खोज करना बहुत आसान हो ... या ऐसा कुछ।
सुल्तान

@ टिप्पणी, नीचे अपनी टिप्पणी के लिए धन्यवाद। मुझे लगता है कि आप उन्हें अपने प्रश्न में जोड़ सकते हैं, या आपके द्वारा प्राप्त किए गए उत्तर के साथ भी पोस्ट कर सकते हैं: "यह डिज़ाइन द्वारा है और इस क्षेत्र में किसी भी प्रासंगिक कीड़े को सुलझा लिया गया है।" तो ऐसा लगता है कि वे इसका कारण नहीं बता सकते या नहीं बताना चाहते।
फेरन मायलिनच

जवाबों:


24

यह स्विफ्ट प्रोग्रामिंग गाइड में निर्दिष्ट "इनिशियलाइज़र चेनिंग" नियमों का नियम 1 है, जो पढ़ता है:

नियम 1: मनोनीत initializers एक कॉल करना होगा नामित उनके तत्काल सुपर क्लास से प्रारंभकर्ता।

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

जोर मेरा। डिज़ाइन किए गए आरंभकर्ता सुविधा प्रारंभकों को कॉल नहीं कर सकते हैं।

एक आरेख है जो नियमों के साथ जाता है कि प्रदर्शनकर्ता को "निर्देश" की अनुमति क्या है:

प्रारंभिक चाइनिंग


80
लेकिन ऐसा क्यों किया जाता है? प्रलेखन केवल यह कहता है कि यह डिज़ाइन को सरल करता है, लेकिन मैं यह नहीं देखता कि यह कैसा मामला है अगर मुझे निर्दिष्ट निर्दिष्टकर्ताओं पर लगातार डिफ़ॉल्ट मान निर्दिष्ट करके खुद को दोहराना पड़ता है, तो मुझे इस बात की कोई परवाह नहीं है कि सुविधा में डिफ़ॉल्ट लोग कब हैं? initializers क्या करेंगे?
रॉबर्ट

5
मैंने इस सवाल के कुछ दिन बाद 17266917 पर एक बग दर्ज किया, जिससे सुविधा आरंभिक कॉल करने में सक्षम होने के लिए कहा गया। अभी तक कोई प्रतिक्रिया नहीं है, लेकिन मैंने अपनी किसी भी अन्य स्विफ्ट बग रिपोर्ट के लिए कोई भी जवाब नहीं दिया है!
रॉबर्ट

8
उम्मीद है कि वे सुविधा इनिशियल्स को कॉल करने की अनुमति देंगे। एसडीके में कुछ वर्गों के लिए एक निश्चित व्यवहार को प्राप्त करने के लिए कोई अन्य तरीका नहीं है, लेकिन सुविधा को शुरुआती कहते हैं। देखें SCNGeometry: आप SCNGeometryElementकेवल सुविधा इनिलाइज़र का उपयोग करके s जोड़ सकते हैं , इसलिए कोई भी इसे इनहेरिट नहीं कर सकता है।
जक

4
यह एक बहुत ही खराब भाषा डिजाइन निर्णय है। अपने नए ऐप की शुरुआत से मैंने अपने उपवर्ग से NSWindowController.init (windowNibName) को स्विफ्ट के साथ विकसित करने का फैसला किया, और मैं अभी ऐसा नहीं कर सकता :(
यूनिकस

4
@Kaiserludi: कुछ भी उपयोगी नहीं है, इसे निम्नलिखित प्रतिक्रिया के साथ बंद कर दिया गया था: "यह डिजाइन द्वारा है और इस क्षेत्र में किसी भी प्रासंगिक कीड़े को हल किया गया है।"
रॉबर्ट

21

विचार करें

class A
{
    var a: Int
    var b: Int

    init (a: Int, b: Int) {
        print("Entering A.init(a,b)")
        self.a = a; self.b = b
    }

    convenience init(a: Int) {
        print("Entering A.init(a)")
        self.init(a: a, b: 0)
    }

    convenience init() {
        print("Entering A.init()")
        self.init(a:0)
    }
}


class B : A
{
    var c: Int

    override init(a: Int, b: Int)
    {
        print("Entering B.init(a,b)")
        self.c = 0; super.init(a: a, b: b)
    }
}

var b = B()

क्योंकि कक्षा A के सभी निर्दिष्ट आरंभीकरण ओवरराइड हैं, वर्ग B, A की सभी सुविधा आरंभीकरणों को विरासत में देगा।

Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)

अब, यदि निर्दिष्ट इनिशियलाइज़र B.init (a: b :) को बेस क्लास सुविधा इनिशियलाइज़र A.init (a :) को कॉल करने की अनुमति होगी, तो इसके परिणामस्वरूप B.init (a: b, b) को एक पुनरावर्ती कॉल मिलेगा। )।


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

@fluidsonic लेकिन वह पागल हो जाएगा क्योंकि आपकी संरचना और कक्षा के तरीकों को बदल दिया जाएगा यह इस बात पर निर्भर करता है कि उनका उपयोग कैसे किया गया था। डिबगिंग मज़ा की कल्पना करो!
kdazzle

2
@kdazzle न तो कक्षा की संरचना और न ही इसकी कक्षा के तरीके बदलेंगे। उन्हें क्यों करना चाहिए? - मैं केवल एक ही समस्या के बारे में सोच सकता हूं कि सुविधा इनिशियलाइज़र को एक उप-वर्ग के निर्दिष्ट इनिशियलाइज़र को डायनामिक रूप से सौंपना होगा जब विरासत में मिला हो और स्टेटिक रूप से यह अपनी क्लास के निर्दिष्ट इनिशियलाइज़र के पास हो, जब विरासत में नहीं मिला हो, लेकिन उप-वर्ग से प्रत्यायोजित किया गया हो।
तरल पदार्थ

मुझे लगता है कि आप सामान्य तरीकों से भी एक समान पुनरावृत्ति समस्या प्राप्त कर सकते हैं। यह आपके निर्णय पर निर्भर करता है कि कॉलिंग के तरीके क्या हैं। उदाहरण के लिए, यह मूर्खतापूर्ण होगा यदि कोई भाषा पुनरावर्ती कॉल की अनुमति नहीं देती क्योंकि आप एक अनंत लूप में आ सकते हैं। प्रोग्रामर्स को समझना चाहिए कि वे क्या कर रहे हैं। :)
फेरन मेनलिच

13

यह इसलिए है क्योंकि आप एक अनंत पुनरावृत्ति के साथ समाप्त हो सकते हैं। विचार करें:

class SuperClass {
    init() {
    }

    convenience init(value: Int) {
        // calls init() of the current class
        // so init() for SubClass if the instance
        // is a SubClass
        self.init()
    }
}

class SubClass : SuperClass {
    override init() {
        super.init(value: 10)
    }
}

और देखो:

let a = SubClass()

जो कॉल SubClass.init()करेगा SuperClass.init(value:)जो कॉल करेगा वो कॉल करेगा SubClass.init()

नामित / सुविधा init नियमों को डिज़ाइन किया गया है कि एक क्लास इनिशियलाइज़ेशन हमेशा सही होगा।


1
हालांकि एक उपवर्ग स्पष्ट रूप से अपने सुपरक्लास से सुविधा इनिशियलाइज़र को नहीं बुला सकता है, यह उन्हें विरासत में दे सकता है, यह देखते हुए कि उप-वर्ग अपने सभी सुपरक्लास नामित इनिशियलाइज़र ( जैसे उदाहरण इस उदाहरण ) के सभी की आपूर्ति की आपूर्ति करता है । इसलिए, ऊपर दिया गया आपका उदाहरण वास्तव में सही है, लेकिन एक विशेष मामला सुपरक्लास की सुविधा इनिशियलाइज़र से स्पष्ट रूप से संबंधित नहीं है, लेकिन इस तथ्य के बजाय कि एक नामित इनिशियलाइज़र एक सुविधा को कॉल नहीं कर सकता है , क्योंकि यह पुनरावर्ती परिदृश्यों जैसे कि ऊपर एक को जन्म देगा ।
dfrib 18

1

मुझे इसके लिए चारों ओर एक काम मिला। यह सुपर सुंदर नहीं है, लेकिन यह सुपरक्लास के मूल्यों को न जानने या डिफ़ॉल्ट मूल्यों को सेट करने की इच्छा की समस्या को हल करता है।

आपको बस इतना करना है कि सुपरक्लास का एक उदाहरण बनाएं, सुविधा का उपयोग करते हुए init, initउपवर्ग में ही। तब आप अपने द्वारा initबनाए गए इंस्टेंस का उपयोग करके सुपर के नामित को बुलाते हैं ।

class A {
    var x: Int

    init(x: Int) {
        self.x = x
    }

    convenience init() {
        self.init(x: 0)
    }
}

class B: A {
    init() {
        // calls A's convenience init, gets instance of A with default x value
        let intermediate = A() 

        super.init(x: intermediate.x) 
    }
}

1

init()एक नए सहायक समारोह में अपने सुविधाजनक से आरंभीकरण कोड निकालने पर विचार foo()करें, foo(...)अपने उप-वर्ग में आरंभीकरण करने के लिए कॉल करें ।


अच्छा सुझाव है, लेकिन यह वास्तव में सवाल का जवाब नहीं है।
स्वाइपनेमेसेक

0

डब्ल्यूडब्ल्यूडीसी-वीडियो "403 इंटरमीडिएट स्विफ्ट" में 18:30 पर शुरुआती और उनके वंशानुक्रम की गहन व्याख्या के लिए देखें। जैसा कि मैंने समझा, निम्नलिखित पर विचार करें:

class Dragon {
    var legs: Int
    var isFlying: Bool

    init(legs: Int, isFlying: Bool) {
        self.legs = legs
        self.isFlying = isFlying
    }

    convenience initWyvern() { 
        self.init(legs: 2, isFlying: true)
    }
}

लेकिन अब एक Wyrm-subclass पर विचार करें: एक Wyrm एक ड्रैगन है जिसके पैर और पंख नहीं होते हैं। तो एक वेवरन (2 पैर, 2 पंखों) के लिए इनिशियल इसके लिए गलत है! उस त्रुटि से बचा जा सकता है यदि सुविधा वायवेर्न-इनिशियलाइज़र को केवल कॉल नहीं किया जा सकता है, लेकिन केवल पूर्ण निर्दिष्ट इनिशियलाइज़र:

class Wyrm: Dragon {
    init() {
        super.init(legs: 0, isFlying: false)
    }
}

12
यह वास्तव में एक कारण नहीं है। क्या होगा अगर मैं एक उपवर्ग बनाऊं जब initWyvernसमझ में आता है?
सुल्तान

4
हाँ, मैं आश्वस्त नहीं हूँ। Wyrmसुविधा प्रारंभकर्ता को कॉल करने के बाद पैरों की संख्या को ओवरराइड करने से कुछ भी नहीं होता है।
रॉबर्ट

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

IMO के अधिवेशन को "सुपर" कहने की परंपरा वैचारिक रूप से नई नहीं है, यह वस्तुनिष्ठ-ग के रूप में ही है, बस वहीं, सब कुछ एक प्रारंभिक मूल्य (nil, 0, जो भी हो) को स्वचालित रूप से सौंपा जा रहा था। इसकी बस यही है कि स्विफ्ट में, हमें इस चरण की ओएस आरंभीकरण खुद करना होगा। इस दृष्टिकोण का एक लाभ यह है कि हमारे पास एक अलग प्रारंभिक मूल्य निर्दिष्ट करने का विकल्प भी है।
रोशन

-1

आपके पास केवल दो इनिशियलाइज़र क्यों नहीं हैं - एक डिफ़ॉल्ट मान के साथ?

class A {
  var x: Int

  init(x: Int) {
    self.x = x
  }

  init() {
    self.x = 0
  }
}

class B: A {
  override init() {
    super.init()

    // Do something else
  }
}

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