स्विफ्ट में सुविधा कीवर्ड की भी आवश्यकता क्यों है?


132

चूंकि स्विफ्ट विधि और इनिलाइज़र ओवरलोडिंग का समर्थन करता है, आप initएक-दूसरे के साथ कई डाल सकते हैं और जो भी आप सुविधाजनक हैं, उसका उपयोग कर सकते हैं:

class Person {
    var name:String

    init(name: String) {
        self.name = name
    }

    init() {
        self.name = "John"
    }
}

तो convenienceकीवर्ड क्यों मौजूद होगा? निम्नलिखित में से क्या बेहतर है?

class Person {
    var name:String

    init(name: String) {
        self.name = name
    }

    convenience init() {
        self.init(name: "John")
    }
}

13
दस्तावेज़ीकरण में बस इस के माध्यम से पढ़ रहा था और इसके बारे में भी भ्रमित हो गया। : /
बोईदकन

जवाबों:


235

मौजूदा उत्तर केवल convenienceकहानी का आधा हिस्सा बताते हैं । कहानी का दूसरा आधा हिस्सा, जो मौजूदा उत्तरों में से कोई भी कवर नहीं करता है, डेसमंड ने टिप्पणियों में पोस्ट किए गए प्रश्न का उत्तर दिया है:

स्विफ्ट मुझे convenienceअपने इनिशियलाइज़र के सामने रखने के लिए मजबूर क्यों करेगा क्योंकि मुझे self.initइससे कॉल करने की आवश्यकता है ? `

मैंने इस उत्तर में इसे थोड़ा सा स्पर्श किया , जिसमें मैंने स्विफ्ट के कई प्रारंभिक नियमों को विवरण में शामिल किया, लेकिन मुख्य फोकस requiredशब्द पर था । लेकिन यह जवाब अभी भी कुछ ऐसा था जो इस प्रश्न और इस उत्तर के लिए प्रासंगिक है। हमें यह समझना होगा कि स्विफ्ट इनिशियलाइज़र इनहेरिटेंस कैसे काम करता है।

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

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

तो इस मामले में:

class Foo {
    var a: Int
}

aएक असंक्रमित उदाहरण चर है। यह तब तक संकलित नहीं होगा जब तक हम aएक डिफ़ॉल्ट मूल्य नहीं देते :

class Foo {
    var a: Int = 0
}

या aएक आरंभिक विधि में आरंभ करें:

class Foo {
    var a: Int

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

अब, देखते हैं कि अगर हम उपवर्ग करते हैं तो क्या होगा Foo?

class Bar: Foo {
    var b: Int

    init(a: Int, b: Int) {
        self.b = b
        super.init(a: a)
    }
}

सही? हमने एक वैरिएबल जोड़ा है, और हमने एक इनिशियलाइज़र जोड़ा है bताकि एक मान सेट किया जा सके ताकि यह संकलित हो जाए। आप किस भाषा से आ रहे हैं, इस पर निर्भर करते हुए, आप उम्मीद कर सकते हैं कि आपको इनिशियलाइज़र Barविरासत में मिला है । लेकिन यह नहीं है। और यह कैसे हो सकता है? कैसे करता है पता है कि कैसे करने के लिए एक मूल्य निर्दिष्ट है कि चर जोड़ा? यह नहीं है इसलिए हम एक ऐसे इनिशियलाइज़र के साथ एक उदाहरण को इनिशियलाइज़ नहीं कर सकते हैं जो हमारे सभी मूल्यों को इनिशियलाइज़ नहीं कर सकता है।Fooinit(a: Int)Fooinit(a: Int)bBarBar

इसका किसी से क्या लेना-देना है convenience?

ठीक है, आइए आरंभिक विरासत पर नियमों को देखें :

नियम 1

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

नियम २

यदि आपका उप-वर्ग अपने सभी सुपरक्लास नामित इनिशियलाइज़र के कार्यान्वयन को प्रदान करता है - या तो उन्हें नियम 1 के अनुसार विरासत में मिला है, या इसकी परिभाषा के भाग के रूप में एक कस्टम कार्यान्वयन प्रदान करता है - तो यह स्वचालित रूप से सभी सुपरक्लास सुविधा इनिशियलाइज़र को विरासत में मिला है।

सूचना नियम 2, जिसमें सुविधा प्रारंभकों का उल्लेख है।

इसलिए convenienceकीवर्ड जो कुछ भी करता है वह हमें इंगित करता है कि कौन से इनिशियलाइज़र को उन उपवर्गों द्वारा विरासत में मिला जा सकता है जो डिफ़ॉल्ट मानों के बिना उदाहरण चर जोड़ते हैं।

आइए इस उदाहरण को लें Base:

class Base {
    let a: Int
    let b: Int

    init(a: Int, b: Int) {
        self.a = a
        self.b = b
    }

    convenience init() {
        self.init(a: 0, b: 0)
    }

    convenience init(a: Int) {
        self.init(a: a, b: 0)
    }

    convenience init(b: Int) {
        self.init(a: 0, b: b)
    }
}

ध्यान दें, हमारे पास तीन हैं convenience इनिशियलाइज़र हैं। इसका मतलब है कि हमारे पास तीन शुरुआती हैं जो विरासत में मिले हैं। और हमारे पास एक निर्दिष्ट इनिशियलाइज़र है (एक निर्दिष्ट इनिशियलाइज़र केवल कोई इनिशियलाइज़र है जो एक सुविधा आरम्भक नहीं है)।

हम चार अलग-अलग तरीकों से बेस क्लास के इंस्टेंस को इंस्टेंट कर सकते हैं:

यहाँ छवि विवरण दर्ज करें

तो, चलो एक उपवर्ग बनाएँ।

class NonInheritor: Base {
    let c: Int

    init(a: Int, b: Int, c: Int) {
        self.c = c
        super.init(a: a, b: b)
    }
}

हमें विरासत में मिली है Base। हमने अपना खुद का इंस्टालेशन वेरिएबल जोड़ा है और हमने इसे डिफॉल्ट वैल्यू नहीं दिया है, इसलिए हमें अपने इनिशियलाइज़र को जोड़ना होगा। हमने एक जोड़ा init(a: Int, b: Int, c: Int), लेकिन यह Baseवर्ग के निर्दिष्ट इनिशियलाइज़र के हस्ताक्षर से मेल नहीं खाता init(a: Int, b: Int):। इसका मतलब है, हम किसी भी शुरुआती से विरासत में नहीं मिल रहे हैं Base:

यहाँ छवि विवरण दर्ज करें

इसलिए, अगर हमें विरासत में मिला है Base, तो क्या होगा , लेकिन हमने आगे बढ़कर एक इनिशियलाइज़र को लागू किया, जो निर्दिष्ट इनिशियलाइज़र से मेल खाता है Base?

class Inheritor: Base {
    let c: Int

    init(a: Int, b: Int, c: Int) {
        self.c = c
        super.init(a: a, b: b)
    }

    convenience override init(a: Int, b: Int) {
        self.init(a: a, b: b, c: 0)
    }
}

अब, हमने इस कक्षा में सीधे लागू किए गए दो इनिशियलाइज़र्स के अलावा, क्योंकि हमने इनिशियलाइज़र मिलान Baseक्लास के निर्दिष्ट इनिशियलाइज़र को लागू किया है, हमें Baseक्लास के सभी convenienceइनिशियलाइज़र्स विरासत में मिलते हैं :

यहाँ छवि विवरण दर्ज करें

तथ्य यह है कि मिलान हस्ताक्षर के साथ इनिशलाइज़र को चिह्नित किया जाता है, convenienceयहाँ कोई अंतर नहीं है। इसका केवल यह अर्थ है कि Inheritorकेवल एक निर्दिष्ट इनिशियलाइज़र है। इसलिए अगर हमें विरासत में मिली है Inheritor, तो हमें बस उस एक नामित इनिशियलाइज़र को लागू करना होगा, और उसके बाद हम Inheritorइनिशियलाइज़र को इनहेरिट करेंगे , जिसका अर्थ है कि हमने सभी Baseइनिशियलाइज़ इनिशियलाइज़र्स को लागू किया है और इसके convenienceइनिशियलाइज़र को इनहेरिट किया जा सकता है।


16
एकमात्र उत्तर जो वास्तव में प्रश्न का उत्तर देता है और डॉक्स का अनुसरण करता है। अगर मैं ओपी होता तो इसे स्वीकार करता।
FreeNickname

12
आपको एक किताब लिखनी चाहिए;)
coolbeet

1
@SLN यह उत्तर स्विफ्ट इनिशियलाइज़र इनहेरिटेंस कैसे काम करता है पर बहुत कुछ शामिल करता है।
nhgrif

1
@SLN एक बार के साथ बनाने क्योंकि init(a: Int)छोड़ना होगा bअन-प्रारंभ।
इयान वारबर्टन

2
@IanWarburton मैं इस विशेष "क्यों" का जवाब नहीं जानता। आपकी टिप्पणी के दूसरे भाग में आपका तर्क मुझे अच्छा लग रहा है, लेकिन प्रलेखन स्पष्ट रूप से बताता है कि यह कैसे काम करता है, और एक खेल के मैदान में जो आप पूछ रहे हैं उसका एक उदाहरण फेंकने से व्यवहार किए गए दस्तावेज़ों से मेल खाने वाले व्यवहार की पुष्टि होती है।
nhgrif

9

ज्यादातर स्पष्टता। आप दूसरे उदाहरण से,

init(name: String) {
    self.name = name
}

आवश्यक या नामित है । यह आपके सभी स्थिरांक और चर को इनिशियलाइज़ करना है। सुविधा इनिशियलाइज़र वैकल्पिक हैं, और आमतौर पर इनिशियलाइज़ेशन को आसान बनाने के लिए उपयोग किया जा सकता है। उदाहरण के लिए, मान लें कि आपके व्यक्ति वर्ग में एक वैकल्पिक चर लिंग है:

var gender: Gender?

जहां लिंग एक एनम है

enum Gender {
  case Male, Female
}

आप इस तरह की सुविधा शुरू कर सकते हैं

convenience init(maleWithName: String) {
   self.init(name: name)
   gender = .Male
}

convenience init(femaleWithName: String) {
   self.init(name: name)
   gender = .Female
}

सुविधा इनिशियलाइज़र को उनमें निर्दिष्ट या आवश्यक इनिशियलाइज़र को कॉल करना होगा । यदि आपकी कक्षा एक उपवर्ग है, तो super.init() इसे आरंभीकरण के भीतर कॉल करना होगा ।


2
इसलिए यह कंपाइलर के लिए पूरी तरह से स्पष्ट होगा कि मैं convenienceकीवर्ड के बिना भी कई इनिशियलाइज़र्स के साथ क्या करने की कोशिश कर रहा हूं, लेकिन स्विफ्ट अभी भी इसके बारे में बग रहा होगा। यह उस तरह की सादगी नहीं है जिसकी मुझे Apple से उम्मीद थी =)
डेसमंड ह्यूम

2
यह उत्तर कुछ भी उत्तर नहीं देता है। आपने कहा "स्पष्टता", लेकिन यह स्पष्ट नहीं किया कि यह कैसे कुछ भी स्पष्ट करता है।
रोबो रोबोक

7

ठीक है, पहली बात मेरे दिमाग में आती है कि यह कोड संगठन और पठनीयता के लिए वर्ग विरासत में उपयोग किया जाता है। अपनी Personकक्षा के साथ आगे बढ़ते हुए , इस तरह से एक परिदृश्य के बारे में सोचें

class Person{
    var name: String
    init(name: String){
        self.name = name
    }

    convenience init(){
        self.init(name: "Unknown")
    }
}


class Employee: Person{
    var salary: Double
    init(name:String, salary:Double){
        self.salary = salary
        super.init(name: name)
    }

    override convenience init(name: String) {
        self.init(name:name, salary: 0)
    }
}

let employee1 = Employee() // {{name "Unknown"} salary 0}
let john = Employee(name: "John") // {{name "John"} salary 0}
let jane = Employee(name: "Jane", salary: 700) // {{name "Jane"} salary 700}

सुविधा इनिशलाइज़र के साथ मैं Employee()बिना किसी मूल्य के एक ऑब्जेक्ट बनाने में सक्षम हूं , इसलिए शब्दconvenience


2
convenienceहटाए गए कीवर्ड के साथ , स्विफ्ट को एक ही सटीक तरीके से व्यवहार करने के लिए पर्याप्त जानकारी नहीं मिलेगी?
डेसमंड ह्यूम

नहीं, यदि आप convenienceकीवर्ड हटाते हैं तो आप Employeeबिना किसी तर्क के ऑब्जेक्ट को इनिशियलाइज़ नहीं कर सकते ।
u54r

विशेष रूप से, कॉलिंग इनिशियलाइज़र के Employee()कारण (विरासत में मिला है convenience) init(), जो कॉल करता है self.init(name: "Unknown")init(name: String), इसके लिए एक सुविधा इनिशियलाइज़र Employee, नामित इनिशियलाइज़र भी कहता है।
BallpointBen

1

जिन बिंदुओं के अलावा अन्य उपयोगकर्ताओं ने यहां बताया है, वह मेरी समझदारी है।

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

उदाहरण के लिए कुछ तृतीय पक्ष वर्ग जिसका आप उपयोग initकरते हैं चार मापदंडों के साथ है लेकिन आपके आवेदन में अंतिम दो का समान मूल्य है। अधिक टाइपिंग से बचने के लिए और अपने कोड को साफ करने के लिए आप convenience initकेवल दो मापदंडों के साथ परिभाषित कर सकते हैं और इसके अंदर self.initअंतिम रूप से डिफ़ॉल्ट मान वाले मापदंडों को कॉल करते हैं।


1
स्विफ्ट मुझे convenienceअपने इनिशियलाइज़र के सामने रखने के लिए मजबूर क्यों करेगा क्योंकि मुझे self.initइससे कॉल करने की आवश्यकता है? यह बेमानी और थोड़े असुविधाजनक लगता है।
डेसमंड ह्यूम

1

स्विफ्ट 2.1 दस्तावेज के अनुसार , convenienceशुरुआती लोगों को कुछ विशिष्ट नियमों का पालन करना होगा:

  1. एक convenienceइनिलाइज़र केवल एक ही वर्ग में इंतिलाइज़र को बुला सकता है, सुपर क्लासेस में (केवल आर-पार नहीं)

  2. एक convenienceइनिलाइज़र को चेन में कहीं एक निर्दिष्ट इनिशियलाइज़र को कॉल करना होता है

  3. एक convenienceइनिलाइज़र किसी भी प्रॉपर्टी को तब तक नहीं बदल सकता है, जब तक कि उसने दूसरे इनिशियलाइज़र को नहीं बुलाया हो - जबकि एक निर्दिष्ट इनिशियलाइज़र को उन गुणों को इनिशियलाइज़ करना होता है, जो किसी दूसरे इनिशियलाइज़र को कॉल करने से पहले करंट क्लास द्वारा पेश किए जाते हैं।

convenienceकीवर्ड का उपयोग करके , स्विफ्ट कंपाइलर जानता है कि इसे इन स्थितियों के लिए जांचना होगा - अन्यथा यह नहीं कर सका।


यकीनन, कंपाइलर शायद convenienceकीवर्ड के बिना इसे सुलझा सकता है ।
nhgrif

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

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

1

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

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