स्विफ्ट डू-ट्राई-कैच सिंटैक्स


162

मैं इसे नई त्रुटि को समझने की कोशिश करता हूं, जो तेजी से हो रही है। 2. यहाँ मैंने जो किया है: मैंने पहली बार एक त्रुटि घोषित की है:

enum SandwichError: ErrorType {
    case NotMe
    case DoItYourself
}

और फिर मैंने एक विधि घोषित की जो एक त्रुटि फेंकता है (अपवाद वाले लोग नहीं। यह एक त्रुटि है।)। यहाँ वह विधि है:

func makeMeSandwich(names: [String: String]) throws -> String {
    guard let sandwich = names["sandwich"] else {
        throw SandwichError.NotMe
    }

    return sandwich
}

समस्या कॉलिंग पक्ष से है। यहाँ इस विधि को कॉल करने वाला कोड है:

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
}

doलाइन कंपाइलर के बाद कहते हैं Errors thrown from here are not handled because the enclosing catch is not exhaustive। लेकिन मेरी राय में यह संपूर्ण है क्योंकि SandwichErrorएनम में केवल दो ही मामले हैं ।

नियमित रूप से स्विच स्टेटमेंट के लिए स्विफ्ट समझ सकता है कि जब हर मामले को संभाला जाता है तो यह बहुत व्यापक होता है।


3
आप उस त्रुटि के प्रकार को निर्दिष्ट नहीं करते हैं जिसे आप फेंकते हैं, इसलिए स्विफ्ट सभी संभावित विकल्पों को निर्धारित नहीं कर सकता है
फ़ारले हेनन

क्या त्रुटि के प्रकार को निर्दिष्ट करने का कोई तरीका है?
मुस्तफा

मुझे स्विफ्ट पुस्तक के नए संस्करण में कुछ भी नहीं मिल रहा है - अभी केवल थ्रो कीवर्ड है
फेरेली हेनन

कोई त्रुटि या चेतावनी के साथ खेल के मैदान में मेरे लिए काम करता है।
फोगमिस्टर

2
खेल के मैदान doशीर्ष स्तर पर ब्लॉक की अनुमति देने के लिए दिखाई देते हैं जो गैर-थकाऊ होते हैं - यदि आप इसे गैर-फेंकने वाले फ़ंक्शन में लपेटते हैं तो यह त्रुटि उत्पन्न करेगा।
सैम

जवाबों:


267

स्विफ्ट 2 त्रुटि हैंडलिंग मॉडल के दो महत्वपूर्ण बिंदु हैं: थकावट और लचीलापन। साथ में, वे हर संभव त्रुटि को पकड़ने के लिए आपके do/ catchकथन को उबालते हैं , न कि केवल उन लोगों को जिन्हें आप जानते हैं कि आप फेंक सकते हैं।

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

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

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch let error {
    print(error.localizedDescription)
}

लेकिन वहां रुकने नहीं दिया। इस लचीलापन विचार के बारे में कुछ और सोचें। जिस तरह से आपने अपने सैंडविच को डिज़ाइन किया है, आपको हर जगह त्रुटियों का वर्णन करना होगा जहाँ आप उनका उपयोग करते हैं। इसका मतलब है कि जब भी आप त्रुटि मामलों के सेट को बदलते हैं, तो आपको हर जगह को बदलना होगा जो उनका उपयोग करता है ... बहुत मज़ेदार नहीं।

अपनी स्वयं की त्रुटि प्रकारों को परिभाषित करने के पीछे का विचार आपको इस तरह की चीजों को केंद्रीकृत करने देना है। आप descriptionअपनी त्रुटियों के लिए एक विधि परिभाषित कर सकते हैं:

extension SandwichError: CustomStringConvertible {
    var description: String {
        switch self {
            case NotMe: return "Not me error"
            case DoItYourself: return "Try sudo"
        }
    }
}

और फिर आपका त्रुटि हैंडलिंग कोड आपके त्रुटि प्रकार को स्वयं का वर्णन करने के लिए कह सकता है - अब हर जगह जहां आप त्रुटियों को संभालते हैं, उसी कोड का उपयोग कर सकते हैं, और भविष्य के संभावित त्रुटि मामलों को भी संभाल सकते हैं।

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch let error as SandwichError {
    print(error.description)
} catch {
    print("i dunno")
}

यह रिपोर्टिंग त्रुटियों के अन्य तरीकों का समर्थन करने के लिए त्रुटि प्रकार (या उन पर एक्सटेंशन) का मार्ग भी प्रशस्त करता है - उदाहरण के लिए, आपके पास अपने त्रुटि प्रकार पर एक एक्सटेंशन हो सकता है जो जानता है कि UIAlertControlleriOS उपयोगकर्ता को त्रुटि की रिपोर्ट करने के लिए कैसे प्रस्तुत किया जाए ।


1
@rickster: क्या आप वास्तव में कंपाइलर त्रुटि को पुन: उत्पन्न कर सकते हैं? मूल कोड मेरे लिए त्रुटियों या चेतावनियों के बिना संकलित है। और अगर एक अनियंत्रित अपवाद को फेंक दिया जाता है, तो कार्यक्रम के साथ गर्भपात होता है। error caught in main()- इसलिए जब तक आपने कहा कि समझदार नहीं है, मैं उस व्यवहार को पुन: पेश नहीं कर सकता।
मार्टिन आर।

5
प्यार करें कि आपने एक्सटेंशन में त्रुटि संदेशों को कैसे अलग किया। अपने कोड को साफ रखने का वास्तव में अच्छा तरीका है! महान उदाहरण!
कोनराड77

यह अत्यधिक अनुशंसा की जाती है कि आप tryउत्पादन कोड में जबरन अभिव्यक्ति का उपयोग करने से बचें क्योंकि यह एक रनटाइम त्रुटि का कारण बन सकता है और आपके आवेदन को क्रैश कर सकता है
ओटार

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

यदि आप सभी की जरूरत है त्रुटि संदेश प्रदर्शित कर रहा है, उस तर्क को SandwichErrorवर्ग के अंदर रखकर समझ में आता है। हालाँकि, मुझे अधिकांश त्रुटियों के लिए संदेह है, तर्क को संभालने में त्रुटि इतनी अतिक्रमित नहीं हो सकती है। ऐसा इसलिए है क्योंकि आम तौर पर इसके लिए कॉलर के संदर्भ के ज्ञान की आवश्यकता होती है (चाहे पुनर्प्राप्त करना हो, या पुन: प्रयास करना हो या विफलता की रिपोर्ट करना हो, आदि)। दूसरे शब्दों में, मुझे संदेह है कि सबसे आम पैटर्न को वैसे भी विशिष्ट त्रुटि प्रकारों के खिलाफ मैच करना होगा।
अधिकतम

29

मुझे संदेह है कि यह अभी तक ठीक से लागू नहीं किया गया है। स्विफ्ट प्रोग्रामिंग गाइड निश्चित रूप से सूचित करते हैं कि संकलक 'एक स्विच बयान की तरह' संपूर्ण मैचों अनुमान लगा सकते हैं लगता है। यह catchसंपूर्ण होने के लिए सामान्य की आवश्यकता का कोई उल्लेख नहीं करता है ।

आप यह भी देखेंगे कि त्रुटि tryलाइन पर है, न कि ब्लॉक के अंत में, यानी किसी बिंदु पर कंपाइलर यह इंगित करने में सक्षम होगा tryकि ब्लॉक में किस स्टेटमेंट में अपवादित अपवाद प्रकार हैं।

प्रलेखन हालांकि थोड़ा अस्पष्ट है। मैंने 'स्विफ्ट में नया क्या है' वीडियो के माध्यम से स्किम्ड किया है और कोई सुराग नहीं मिल सका है; मैं कोशिश करता रहूँगा।

अपडेट करें:

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

बीटा 4 अपडेट:

Xcode 7b4 के लिए डॉक कमेंट सपोर्ट जोड़ा गया है Throws:, जिसका उपयोग "यह करने के लिए किया जाना चाहिए कि त्रुटियों को क्या और क्यों फेंका जा सकता है"। मुझे लगता है कि यह कम से कम एपीआई उपभोक्ताओं को त्रुटियों को संप्रेषित करने के लिए कुछ तंत्र प्रदान करता है। जब आपके पास प्रलेखन है तो एक प्रकार की प्रणाली की आवश्यकता है!

एक और अपडेट:

कुछ समय बिताने के बाद स्वत: ErrorTypeनिष्कासन की आशा करना , और काम करना कि उस मॉडल की क्या सीमाएँ होंगी, मैंने अपना विचार बदल दिया है - यह वही है जो मुझे लगता है कि इसके बजाय एप्पल लागू करता है। अनिवार्य रूप से:

// allow us to do this:
func myFunction() throws -> Int

// or this:
func myFunction() throws CustomError -> Int

// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int

फिर भी एक और अद्यतन

Apple का एरर हैंडलिंग राशन अब यहाँ उपलब्ध हैस्विफ्ट-इवोल्यूशन मेलिंग सूची पर कुछ दिलचस्प चर्चाएं भी हुई हैं । अनिवार्य रूप से, जॉन मैक्कल टाइप की त्रुटियों के विरोध में है क्योंकि उनका मानना ​​है कि अधिकांश पुस्तकालय वैसे भी एक सामान्य त्रुटि मामले सहित समाप्त हो जाएंगे, और टाइप किए गए त्रुटियों को बॉयलरप्लेट के अलावा कोड में बहुत अधिक जोड़ने की संभावना नहीं है (उन्होंने 'एस्पिरेशनल कफ' शब्द का इस्तेमाल किया है)। क्रिस Lattner ने कहा कि वह स्विफ्ट 3 में टाइप की गई त्रुटियों के लिए खुला है अगर यह लचीलापन मॉडल के साथ काम कर सकता है।


लिंक के लिए धन्यवाद। हालांकि जॉन द्वारा राजी नहीं किया गया: "कई पुस्तकालयों में 'अन्य त्रुटि' प्रकार शामिल हैं" का मतलब यह नहीं है कि हर किसी को "अन्य त्रुटि" प्रकार की आवश्यकता होती है।
फ्रैंकलिन यू

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

4

स्विफ्ट चिंता है कि आपका केस स्टेटमेंट सभी मामलों को कवर नहीं कर रहा है, इसे ठीक करने के लिए आपको एक डिफ़ॉल्ट केस बनाने की आवश्यकता है:

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch Default {
    print("Another Error")
}

2
लेकिन यह अजीब नहीं है? मेरे पास केवल दो मामले हैं और उनमें से सभी catchबयानों में सूचीबद्ध हैं ।
मुस्तफा

2
अब बढ़ाने के अनुरोध के लिए एक अच्छा समय है func method() throws(YourErrorEnum), या यहां तक throws(YourEnum.Error1, .Error2, .Error3)कि आप जानते हैं कि क्या फेंका जा सकता है
Matthias Bauch

8
WWDC के सत्रों में से एक पर स्विफ्ट कंपाइलर टीम ने स्पष्ट किया कि वे सभी संभावित त्रुटियों 'जावा' की पांडित्य सूची नहीं चाहते हैं।
सैम

4
कोई डिफ़ॉल्ट / डिफ़ॉल्ट त्रुटि नहीं है; बस एक खाली कैच {} छोड़ दें क्योंकि अन्य पोस्टरों ने बताया है
ओपूस 1217

1
@Iroro जो मुझे सुरक्षित नहीं बनाता है; अगर मैं भविष्य में "सरणी में एक नई प्रविष्टि जोड़ें", तो सभी प्रभावित कैच क्लॉस को अपडेट नहीं करने के लिए संकलक को मुझ पर चिल्लाना चाहिए।
फ्रैंकलिन यू

3

मैं एक प्रकार के फ़ंक्शन को फेंकने की कमी से भी निराश था, लेकिन मुझे यह अब @rickster के लिए धन्यवाद मिलता है और मैं इसे इस तरह संक्षेप में प्रस्तुत करूंगा: मान लीजिए कि हम फ़ंक्शन के प्रकार को निर्दिष्ट कर सकते हैं, हमारे पास कुछ इस तरह होगा:

enum MyError: ErrorType { case ErrorA, ErrorB }

func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }

do {
    try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }

समस्या यह है कि भले ही हम myFunctionThatTrows में कुछ भी नहीं बदलते हैं, अगर हम सिर्फ Myrrr पर एक त्रुटि मामला जोड़ते हैं:

enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }

हमें डराया जाता है क्योंकि हमारा कार्य / प्रयास / कैच अब संपूर्ण नहीं है, साथ ही साथ किसी अन्य स्थान पर भी है जहाँ हमने फ़ंक्शन को कॉल किया है जो EEEror


3
मुझे यकीन नहीं है कि आप क्यों खराब कर रहे हैं। आपको एक संकलक त्रुटि मिलेगी, जो कि आप चाहते हैं, ठीक है? यदि आप एक एनम केस जोड़ते हैं तो स्टेटमेंट को स्विच करने के लिए क्या होता है।
सैम

एक अर्थ में यह मेरे लिए सबसे अधिक संभावना थी कि यह त्रुटि एनमोंस / डू केस के साथ होगा, लेकिन यह बिल्कुल वैसा ही है जैसा कि एनमोंस / स्विच में होता है, आप सही हैं। मैं अभी भी खुद को समझाने की कोशिश कर रहा हूं कि हम जो फेंकते हैं, उसे टाइप करने के लिए एप्पल की पसंद अच्छा नहीं है, लेकिन आप इस पर मेरी मदद नहीं करते हैं! ^ ^
greg3z

गैर-तुच्छ मामलों में एक बड़ी गड़बड़ी के रूप में मैन्युअल रूप से टाइप की गई त्रुटियां समाप्त हो जाएंगी। प्रकार सभी फेंक से सभी संभावित त्रुटियों का एक संघ हैं और फ़ंक्शन के भीतर बयान की कोशिश करते हैं। यदि आप मैन्युअल रूप से त्रुटि एनम बनाए रख रहे हैं तो यह दर्दनाक होगा। एक catch {}हर ब्लॉक के निचले भाग में यद्यपि यकीनन बदतर है। मुझे उम्मीद है कि संकलक अंततः त्रुटि प्रकारों को स्वचालित रूप से अनुमान लगाएगा जहां यह हो सकता है, लेकिन मैं पुष्टि करने में सक्षम नहीं हूं।
सैम

मैं सहमत हूं कि संकलक को, सैद्धांतिक रूप से, फ़ंक्शन के फेंके जाने वाले त्रुटि प्रकारों का अनुमान लगाने में सक्षम होना चाहिए। लेकिन मुझे लगता है कि यह स्पष्ट रूप से स्पष्टता के लिए उन्हें लिखने के लिए देव के लिए भी समझ में आता है। जिन गैर-तुच्छ मामलों में आप बात कर रहे हैं, विभिन्न प्रकार की त्रुटि को सूचीबद्ध करना मुझे ठीक लगता है: func f () फेंकता है ErrorTypeA, ErrorTypeB {}
greg3z

इसमें निश्चित रूप से एक बड़ा हिस्सा गायब है जिसमें त्रुटि प्रकारों (डॉक्स टिप्पणियों के अलावा) को संप्रेषित करने की कोई व्यवस्था नहीं है। हालांकि, स्विफ्ट टीम ने कहा है कि वे त्रुटि प्रकारों की स्पष्ट सूची नहीं चाहते हैं। मुझे यकीन है कि ज्यादातर लोग जो अतीत में जावा जाँच अपवादों से निपट चुके हैं, वे सहमत होंगे've
Sam

1
enum NumberError: Error {
  case NegativeNumber(number: Int)
  case ZeroNumber
  case OddNumber(number: Int)
}

extension NumberError: CustomStringConvertible {
         var description: String {
         switch self {
             case .NegativeNumber(let number):
                 return "Negative number \(number) is Passed."
             case .OddNumber(let number):
                return "Odd number \(number) is Passed."
             case .ZeroNumber:
                return "Zero is Passed."
      }
   }
}

 func validateEvenNumber(_ number: Int) throws ->Int {
     if number == 0 {
        throw NumberError.ZeroNumber
     } else if number < 0 {
        throw NumberError.NegativeNumber(number: number)
     } else if number % 2 == 1 {
         throw NumberError.OddNumber(number: number)
     }
    return number
}

अब मान्य संख्या:

 do {
     let number = try validateEvenNumber(0)
     print("Valid Even Number: \(number)")
  } catch let error as NumberError {
     print(error.description)
  }

-2

इस तरह से एनम बनाएं:

//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}

जैसे विधि बनाएँ:

 func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
    throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
    throw spendingError.limit
}
return morningSpending + eveningSpending
}

अब जाँच त्रुटि है या नहीं और इसे संभाल:

do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}

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