स्विफ्ट में एक फाल्ट इनिशियलाइज़र को लागू करने के लिए सबसे अच्छा अभ्यास


100

निम्नलिखित कोड के साथ मैं एक साधारण मॉडल वर्ग को परिभाषित करने की कोशिश करता हूं और यह फाल्ट इनिशियलाइज़र है, जो पैरामीटर के रूप में एक (json-) शब्दकोश लेता है। nilयदि उपयोगकर्ता नाम मूल json में परिभाषित नहीं है, तो initializer को वापस लौटना चाहिए ।

1. कोड संकलन क्यों नहीं करता है? त्रुटि संदेश कहता है:

एक क्लास इंस्टेंस के सभी संग्रहीत गुणों को एक इनिशलाइज़र से शून्य पर लौटने से पहले आरंभीकृत किया जाना चाहिए।

इसका कोई मतलब नहीं है। जब मैं लौटने की योजना बना रहा हूं तो मुझे उन गुणों को क्यों शुरू करना चाहिएnil ?

2. क्या मेरा दृष्टिकोण सही है या मेरे लक्ष्य को प्राप्त करने के लिए अन्य विचार या सामान्य पैटर्न होंगे?

class User: NSObject {

    let userName: String
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init?(dictionary: NSDictionary) {
        if let value: String = dictionary["user_name"] as? String {
            userName = value
        }
        else {
           return nil
        }

        if let value: Bool = dictionary["super_user"] as? Bool {
            isSuperUser = value
        }

        someDetails = dictionary["some_details"] as? Array

        super.init()
    }
}

मेरे पास एक समान मुद्दा था, मेरे साथ मैंने निष्कर्ष निकाला कि प्रत्येक शब्दकोश मूल्य की उम्मीद की जानी चाहिए और इसलिए मैं मूल्यों को उजागर नहीं करता। अगर संपत्ति नहीं है, तो मैं बग को पकड़ पाऊंगा। इसके अतिरिक्त, मैंने एक canSetCalculablePropertiesबूलियन पैरामीटर जोड़ा, जो मेरे शुरुआती को उन संपत्तियों की गणना करने की अनुमति देता है जो मक्खी पर बनाई जा सकती हैं या नहीं बनाई जा सकती हैं। उदाहरण के लिए, यदि कोई dateCreatedकुंजी गायब है और मैं संपत्ति को मक्खी पर सेट कर सकता हूं क्योंकि canSetCalculablePropertiesपैरामीटर सत्य है, तो मैं इसे वर्तमान तिथि पर सेट करता हूं।
एडम कार्टर

जवाबों:


71

अपडेट: से स्विफ्ट 2.2 परिवर्तन लॉग (21 मार्च, वर्ष 2016 को जारी):

पदनामित या फेंकने के रूप में घोषित नामित वर्ग इनिशियलाइज़र अब ऑब्जेक्ट को पूरी तरह से इनिशियलाइज़ किए जाने से पहले, क्रमशः शून्य वापस कर सकते हैं या एक त्रुटि फेंक सकते हैं।


स्विफ्ट 2.1 और उससे पहले के लिए:

Apple के दस्तावेज़ीकरण (और आपकी संकलक त्रुटि) के अनुसार, एक वर्ग को अपने सभी संग्रहित गुणों को nilआरंभिक संपीड़ित से लौटने से पहले प्रारंभ करना होगा:

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

नोट: यह वास्तव में संरचनाओं और गणनाओं के लिए ठीक काम करता है, केवल कक्षाएं नहीं।

संग्रहित संपत्तियों को संभालने का सुझाया गया तरीका जो कि आरंभिक विफल होने से पहले आरंभीकृत नहीं किया जा सकता है, उन्हें घोषित रूप से अपरिवर्तित वैकल्पिक के रूप में घोषित करना है।

डॉक्स से उदाहरण:

class Product {
    let name: String!
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

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

आपके मामले में, तथापि, बस को परिभाषित करने userNameके रूप में एक String!संकलन त्रुटि को ठीक नहीं है, क्योंकि आप अभी भी अपने आधार वर्ग पर गुण आरंभ के बारे में चिंता करने की जरूरत है, NSObject। सौभाग्य से, userNameएक के रूप में परिभाषित के साथ String!, आप वास्तव में super.init()आपके सामने कॉल कर सकते हैं return nilजो आपके NSObjectआधार वर्ग को सम्मिलित करेगा और संकलन त्रुटि को ठीक करेगा।

class User: NSObject {

    let userName: String!
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init?(dictionary: NSDictionary) {
        super.init()

        if let value = dictionary["user_name"] as? String {
            self.userName = value
        }
        else {
            return nil
        }

        if let value: Bool = dictionary["super_user"] as? Bool {
            self.isSuperUser = value
        }

        self.someDetails = dictionary["some_details"] as? Array
    }
}

1
न केवल आपका बहुत-बहुत धन्यवाद, बल्कि यह भी अच्छी तरह से समझाया गया है
काई हुप्पमन

9
स्विफ्ट 1.2 में, डॉक्स से उदाहरण एक त्रुटि करता है "एक इनिशियलाइज़र से शून्य लौटने से पहले एक वर्ग उदाहरण के सभी संग्रहीत गुणों को आरंभीकृत किया जाना चाहिए"
जेफ्री

2
@ जेफ्री यह सही है, दस्तावेज़ ( Productवर्ग) से उदाहरण एक विशिष्ट मूल्य निर्दिष्ट करने से पहले एक प्रारंभिक विफलता को ट्रिगर नहीं कर सकता है, भले ही डॉक्स का कहना है कि यह कर सकता है। डॉक्स नवीनतम स्विफ्ट संस्करण के साथ सिंक में नहीं हैं। इसके varबजाय इसे अभी के लिए बनाने की सलाह दी जाती है letस्रोत: क्रिस लैटनर
अर्जन

1
दस्तावेज़ में कोड का यह टुकड़ा थोड़ा अलग है: आप पहले संपत्ति सेट करते हैं, और फिर जांचें कि क्या यह मौजूद है। "कक्षाओं के लिए अनुपलब्ध शुरुआती", "स्विफ्ट प्रोग्रामिंग भाषा" देखें। `` `वर्ग उत्पाद {नाम दें: स्ट्रिंग! init; (नाम: स्ट्रिंग) {स्व .नाम = नाम अगर नाम। विस खाली {वापसी nil}}}} `` `
मिशा

मैं इसे Apple डॉक्स में भी पढ़ता हूं लेकिन मैं यह देखने में विफल हूं कि इसकी आवश्यकता क्यों होगी। एक असफलता का मतलब होगा वैसे भी शून्य लौटना, इससे क्या फर्क पड़ता है कि क्या गुणों को आरंभीकृत किया गया है?
Alper

132

इसका कोई मतलब नहीं है। जब मैं शून्य पर लौटने की योजना बनाऊंगा, तो मुझे उन संपत्तियों को क्यों शुरू करना चाहिए?

क्रिस लैटनर के अनुसार यह एक बग है। यहाँ वह कहता है:

यह स्विफ्ट 1.1 संकलक में एक कार्यान्वयन सीमा है, जिसे रिलीज़ नोट्स में प्रलेखित किया गया है। कंपाइलर वर्तमान में सभी मामलों में आंशिक रूप से प्रारंभिक कक्षाओं को नष्ट करने में असमर्थ है, इसलिए यह एक ऐसी स्थिति के गठन को रोक देता है जहां इसे करना होगा। हम इसे भविष्य के रिलीज में तय किए जाने वाले बग को मानते हैं, न कि किसी फीचर को।

स्रोत

संपादित करें:

इसलिए स्विफ्ट अब खुला स्रोत है और इस चैंज के अनुसार यह स्विफ्ट 2.2 के स्नैपशॉट में अब तय हो गया है

पदनामित या फेंकने के रूप में घोषित नामित वर्ग इनिशियलाइज़र अब ऑब्जेक्ट को पूरी तरह से इनिशियलाइज़ किए जाने से पहले, क्रमशः शून्य वापस कर सकते हैं या एक त्रुटि फेंक सकते हैं।


2
मेरी बात को संबोधित करने के लिए धन्यवाद कि गुणों को प्रारंभिक करने का विचार जो किसी भी ज़रूरत से ज़्यादा नहीं होगा, बहुत उचित नहीं लगता है। और एक स्रोत को साझा करने के लिए +1, जो साबित करता है कि क्रिस लैटनर को ऐसा लगता है जैसे मैं करता हूं;)।
काई हुप्पमन

22
FYI करें: "वास्तव में। यह अभी भी कुछ है जिसे हम सुधारना चाहते हैं, लेकिन स्विफ्ट 1.2 के लिए कटौती नहीं की है"। - क्रिस
लैटनर

14
FYI करें: स्विफ्ट 2.0 बीटा 2 में यह अभी भी एक मुद्दा है, और यह एक शुरुआती के साथ एक मुद्दा भी है जो फेंकता है।
अरणासौरस

7

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

class User: NSObject {
    let userName: String = ""
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init?(dictionary: [String: AnyObject]) {
        if let user_name = dictionary["user_name"] as? String {
            userName = user_name
        }

        if let value: Bool = dictionary["super_user"] as? Bool {
            isSuperUser = value
        }

        someDetails = dictionary["some_details"] as? Array

        super.init()

        if userName.isEmpty {
            return nil
        }
    }
}

धन्यवाद, लेकिन मैं यह नहीं देखता कि माइक के जवाब से मजबूत प्रकार के सिस्टम के विचार कैसे भ्रष्ट हैं। सभी में आप अंतर के साथ एक ही समाधान प्रस्तुत करते हैं कि प्रारंभिक मूल्य शून्य के बजाय "" पर सेट है। इसके अलावा, आप एक उपयोगकर्ता नाम के रूप में "" का उपयोग करने के लिए दूर ले जाते हैं (जो कि अकादमिक रूप से काफी अच्छा लग सकता है, लेकिन कम से कम यह अलग नहीं है कि इसे json / शब्दकोश में सेट नहीं किया जा सकता है)
काई हुप्पमन

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

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

6

सीमा को दरकिनार करने का दूसरा तरीका यह है कि आरंभीकरण करने के लिए एक क्लास-फ़ंक्शंस के साथ काम करें। आप उस फ़ंक्शन को एक्सटेंशन में ले जाना चाह सकते हैं:

class User: NSObject {

    let username: String
    let isSuperUser: Bool
    let someDetails: [String]?

    init(userName: String, isSuperUser: Bool, someDetails: [String]?) {

         self.userName = userName
         self.isSuperUser = isSuperUser
         self.someDetails = someDetails

         super.init()
    }
}

extension User {

    class func fromDictionary(dictionary: NSDictionary) -> User? {

        if let username: String = dictionary["user_name"] as? String {

            let isSuperUser = (dictionary["super_user"] as? Bool) ?? false
            let someDetails = dictionary["some_details"] as? [String]

            return User(username: username, isSuperUser: isSuperUser, someDetails: someDetails)
        }

        return nil
    }
}

इसका उपयोग करना बन जाएगा:

if let user = User.fromDictionary(someDict) {

     // Party hard
}

1
यह मुझे पंसद है; मुझे लगता है कि निर्माता चाहते हैं कि वे क्या चाहते हैं, इस बारे में पारदर्शी हों और एक शब्दकोश में पास होना बहुत अपारदर्शी है।
बेन लेगीरियो

3

हालाँकि स्विफ्ट 2.2 जारी कर दिया गया है और अब आपको इनिशियलाइज़र को विफल करने से पहले ऑब्जेक्ट को पूरी तरह से इनिशियलाइज़ नहीं करना है, आपको https://bugs.swift.org/browse/SR-704 तय होने तक अपने घोड़ों को रखना होगा ।


1

मैं इस में पता चला सकते हैं स्विफ्ट 1.2 में किया सकता है

कुछ शर्तें हैं:

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

उदाहरण:

class ClassName: NSObject {

    let property: String!

    init?(propertyValue: String?) {

        self.property = propertyValue

        super.init()

        if self.property == nil {
            return nil
        }
    }
}

0

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

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

अंश से: Apple Inc. " स्विफ्ट प्रोग्रामिंग लैंग्वेज। IBooks। https://itun.es/sg/jEUH0.l


0

आप सुविधा init का उपयोग कर सकते हैं :

class User: NSObject {
    let userName: String
    let isSuperUser: Bool = false
    let someDetails: [String]?

    init(userName: String, isSuperUser: Bool, someDetails: [String]?) {
        self.userName = userName
        self.isSuperUser = isSuperUser
        self.someDetails = someDetails
    }     

    convenience init? (dict: NSDictionary) {            
       guard let userName = dictionary["user_name"] as? String else { return nil }
       guard let isSuperUser = dictionary["super_user"] as? Bool else { return nil }
       guard let someDetails = dictionary["some_details"] as? [String] else { return nil }

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