यह उत्तर सामुदायिक विकि है । यदि आपको लगता है कि इसे बेहतर बनाया जा सकता है, तो इसे संपादित करने के लिए स्वतंत्र महसूस करें !
पृष्ठभूमि: एक वैकल्पिक क्या है?
स्विफ्ट में, Optional
एक सामान्य प्रकार है जिसमें किसी भी प्रकार का मान (या किसी भी प्रकार का) हो सकता है।
कई अन्य प्रोग्रामिंग भाषाओं में, एक विशेष "प्रहरी" मूल्य का उपयोग अक्सर मूल्य की कमी को इंगित करने के लिए किया जाता है । उद्देश्य-सी में, उदाहरण के लिए, nil
( अशक्त सूचक ) किसी वस्तु की कमी को दर्शाता है। लेकिन यह और अधिक मुश्किल हो जाता है जब आदिम प्रकारों के साथ काम करना - -1
पूर्णांक की अनुपस्थिति को इंगित करने के लिए उपयोग किया जाना चाहिए , या शायद INT_MIN
, या कुछ पूर्णांक? यदि किसी विशेष मूल्य को "नो पूर्णांक" के लिए चुना जाता है, तो इसका मतलब है कि इसे अब वैध मान के रूप में नहीं माना जा सकता है ।
स्विफ्ट एक प्रकार की सुरक्षित भाषा है, जिसका अर्थ है कि भाषा आपको उन मूल्यों के बारे में स्पष्ट होने में मदद करती है जिनके साथ आपका कोड काम कर सकता है। अगर आपके कोड का हिस्सा स्ट्रिंग की अपेक्षा करता है, तो टाइप सुरक्षा आपको इसे गलती से इंट पास करने से रोकती है।
स्विफ्ट में, किसी भी प्रकार को वैकल्पिक बनाया जा सकता है । एक वैकल्पिक मूल्य मूल प्रकार, या विशेष मूल्य से किसी भी मूल्य पर ले सकता है nil
।
वैकल्पिक ?
प्रकार पर एक प्रत्यय के साथ परिभाषित किया गया है:
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
एक वैकल्पिक में एक मूल्य की कमी से संकेत मिलता है nil
:
anOptionalInt = nil
(ध्यान दें कि यह ऑब्जेक्टिव-सी में nil
समान नहीं nil
है। ऑब्जेक्टिव-सी में, ऑब्जेक्ट ऑब्जेक्ट पॉइंटरnil
की अनुपस्थिति है , स्विफ्ट में, ऑब्जेक्ट्स / रेफरेंस टाइप्स के लिए ऑप्शनल को प्रतिबंधित नहीं किया जाता है। ऑप्शनल हील की शायद इसी तरह का व्यवहार करता है ।)
मुझे एक वैकल्पिक मूल्य को उजागर करते समय " घातक त्रुटि: अप्रत्याशित रूप से शून्य क्यों मिला ?"
वैकल्पिक मूल्य तक पहुंचने के लिए (यदि इसमें एक भी है), तो आपको इसे खोलना होगा। एक वैकल्पिक मूल्य सुरक्षित रूप से या जबरन खींचा जा सकता है। यदि आप एक वैकल्पिक को बल देते हैं, और इसका कोई मूल्य नहीं है, तो आपका कार्यक्रम उपरोक्त संदेश के साथ क्रैश हो जाएगा।
कोड की एक पंक्ति को उजागर करके Xcode आपको दुर्घटना दिखाएगा। इस लाइन पर समस्या होती है।
यह दुर्घटना दो अलग-अलग प्रकार के फोर्स-अनप्रैप के साथ हो सकती है:
1. स्पष्ट बल खोलना
यह !
एक वैकल्पिक पर ऑपरेटर के साथ किया जाता है । उदाहरण के लिए:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
घातक त्रुटि: वैकल्पिक मूल्य को खोलते समय अप्रत्याशित रूप से शून्य पाया गया
जैसा anOptionalString
कि nil
यहां है, आपको उस लाइन पर एक दुर्घटना मिलेगी जहां आप इसे खोलते हैं।
2. अवैध रूप से अनचाहे वैकल्पिक
इन्हें एक प्रकार के !
बजाय एक के साथ परिभाषित किया ?
गया है।
var optionalDouble: Double! // this value is implicitly unwrapped wherever it's used
इन वैकल्पिकों को एक मान माना जाता है। इसलिए जब भी आप एक अंतर्निहित अलिखित वैकल्पिक का उपयोग करते हैं, तो यह स्वचालित रूप से आपके लिए अपरिवर्तित हो जाएगा। यदि इसमें कोई मान नहीं है, तो यह क्रैश हो जाएगा।
print(optionalDouble) // <- CRASH
घातक त्रुटि: एक वैकल्पिक मान को स्पष्ट करते हुए अप्रत्याशित रूप से शून्य पाया गया
यह पता लगाने के लिए कि क्रैश किस चर के कारण हुआ, आप ⌥परिभाषा दिखाने के लिए क्लिक करते समय पकड़ सकते हैं , जहाँ आपको वैकल्पिक प्रकार मिल सकता है।
IBOutlets, विशेष रूप से, आमतौर पर अंतर्निहित अलिखित वैकल्पिक हैं। ऐसा इसलिए है क्योंकि आपका xib या स्टोरीबोर्ड प्रारंभ के बाद , रनटाइम पर आउटलेट को लिंक करेगा । इसलिए आपको यह सुनिश्चित करना चाहिए कि आपके द्वारा लोड किए जाने से पहले आप आउटलेट्स तक नहीं पहुंच रहे हैं। आपको यह भी जांचना चाहिए कि कनेक्शन आपके स्टोरीबोर्ड / xib फ़ाइल में सही हैं, अन्यथा मान nil
रनटाइम पर होंगे , और इसलिए जब वे निहित होते हैं तो दुर्घटनाग्रस्त हो जाते हैं । कनेक्शन ठीक करते समय, अपने आउटलेट को परिभाषित करने वाले कोड की पंक्तियों को हटाने का प्रयास करें, फिर उन्हें फिर से कनेक्ट करें।
जब मैं कभी भी एक वैकल्पिक खोलना मजबूर करना चाहिए?
स्पष्ट बल खोलना
एक सामान्य नियम के रूप में, आपको स्पष्ट रूप से !
ऑपरेटर के साथ एक विकल्प को खोलना कभी भी मजबूर नहीं करना चाहिए । ऐसे मामले हो सकते हैं जहां उपयोग !
करना स्वीकार्य है - लेकिन आपको केवल इसका उपयोग करना चाहिए यदि आप 100% सुनिश्चित हैं कि वैकल्पिक में एक मूल्य है।
जबकि एक ऐसा अवसर हो सकता है जहां आप बल प्रयोग को समाप्त कर सकते हैं, जैसा कि आप इस तथ्य के लिए जानते हैं कि एक वैकल्पिक में एक मान होता है - एक जगह नहीं है जहां आप सुरक्षित रूप से उस वैकल्पिक को खोल नहीं सकते।
अवैध रूप से अनचाहे वैकल्पिक
ये चर ऐसे डिज़ाइन किए गए हैं कि आप अपने कोड में बाद तक उनके असाइनमेंट को स्थगित कर सकते हैं। यह है अपने सुनिश्चित करने के लिए वे एक मूल्य है इससे पहले कि आप उन तक पहुँच जिम्मेदारी। हालाँकि, क्योंकि उनमें बल प्रयोग शामिल नहीं है, फिर भी वे स्वाभाविक रूप से असुरक्षित हैं - क्योंकि वे मान लेते हैं कि आपका मूल्य गैर-शून्य है, भले ही शून्य असाइन करना मान्य है।
आपको अंतिम उपाय के रूप में केवल अंतर्निहित अलिखित वैकल्पिक का उपयोग करना चाहिए । यदि आप एक आलसी चर का उपयोग कर सकते हैं , या एक चर के लिए एक डिफ़ॉल्ट मान प्रदान कर सकते हैं - तो आपको एक अंतर्निहित अनपेक्षित वैकल्पिक का उपयोग करने के बजाय ऐसा करना चाहिए।
हालांकि, कुछ परिदृश्य हैं जहां निहित अलिखित वैकल्पिक लाभकारी हैं , और आप अभी भी नीचे सूचीबद्ध के रूप में उन्हें सुरक्षित रूप से अलग करने के विभिन्न तरीकों का उपयोग करने में सक्षम हैं - लेकिन आपको हमेशा सावधानी के साथ उनका उपयोग करना चाहिए ।
मैं वैकल्पिक रूप से कैसे निपट सकता हूं?
यह जांचने का सबसे सरल तरीका है कि वैकल्पिक में कोई मान है या नहीं, इसकी तुलना करना है nil
।
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
हालांकि, 99.9% समय जब वैकल्पिक के साथ काम करते हैं, तो आप वास्तव में इसके मूल्य को एक्सेस करना चाहते हैं, अगर इसमें एक भी शामिल हो। ऐसा करने के लिए, आप वैकल्पिक बाइंडिंग का उपयोग कर सकते हैं ।
वैकल्पिक बंधन
वैकल्पिक बाइंडिंग आपको यह जांचने की अनुमति देती है कि क्या वैकल्पिक में कोई मान है - और आपको एक नए चर या स्थिर पर अलिखित मान असाइन करने की अनुमति देता है। यह सिंटैक्स का उपयोग करता है if let x = anOptional {...}
या if var x = anOptional {...}
, यदि आपको इसे बाध्य करने के बाद नए चर के मूल्य को संशोधित करने की आवश्यकता है।
उदाहरण के लिए:
if let number = anOptionalInt {
print("Contains a value! It is \(number)!")
} else {
print("Doesn’t contain a number")
}
यह क्या करता है पहले जांचें कि वैकल्पिक में एक मूल्य है। यदि यह करता है , तो 'अलिखित' मान एक नए चर ( number
) को सौंपा गया है - जिसे आप तब स्वतंत्र रूप से उपयोग कर सकते हैं जैसे कि यह गैर-वैकल्पिक था। यदि वैकल्पिक में कोई मान नहीं है, तो अन्य क्लॉज़ को लागू किया जाएगा, जैसा कि आप उम्मीद करेंगे।
वैकल्पिक बंधन के बारे में क्या साफ है, क्या आप एक ही समय में कई वैकल्पिकों को खोल सकते हैं। आप केवल अल्पविराम से बयानों को अलग कर सकते हैं। यदि सभी वैकल्पिकों को हटा दिया गया था, तो बयान सफल होगा।
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: \(number). And so does anOptionalString, it’s: \(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
एक और साफ-सुथरी चाल यह है कि आप कॉमा का उपयोग मूल्य पर एक निश्चित स्थिति की जांच करने के लिए भी कर सकते हैं, इसे अंजाम देने के बाद।
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}
एक इफ स्टेटमेंट के भीतर वैकल्पिक बाइंडिंग का उपयोग करने के साथ एकमात्र कैच यह है कि आप केवल स्टेटमेंट के दायरे में मौजूद अलिखित मान को एक्सेस कर सकते हैं। यदि आपको बयान के दायरे के बाहर से मूल्य तक पहुंच की आवश्यकता है, तो आप एक गार्ड स्टेटमेंट का उपयोग कर सकते हैं ।
एक गार्ड स्टेटमेंट आपको सफलता के लिए एक शर्त को परिभाषित करने की अनुमति देता है - और वर्तमान स्कोप केवल उस स्थिति को पूरा करने के लिए निष्पादित करना जारी रखेगा। उन्हें वाक्य रचना के साथ परिभाषित किया गया है guard condition else {...}
।
इसलिए, वैकल्पिक बंधन के साथ उनका उपयोग करने के लिए, आप यह कर सकते हैं:
guard let number = anOptionalInt else {
return
}
(ध्यान दें कि गार्ड बॉडी के भीतर, आपको वर्तमान में निष्पादित कोड के दायरे से बाहर निकलने के लिए कंट्रोल ट्रांसफर स्टेटमेंट्स में से एक का उपयोग करना होगा )।
यदि anOptionalInt
कोई मान होता है, तो इसे बिना किसी अपवाद के अनलॉक्ड किया जाएगा और नए number
स्थिरांक को सौंपा जाएगा । गार्ड के बाद कोड तब निष्पादित करना जारी रखेगा। यदि इसमें कोई मान नहीं है - गार्ड कोष्ठक के भीतर कोड को निष्पादित करेगा, जिससे नियंत्रण स्थानांतरित हो जाएगा, ताकि उसके तुरंत बाद कोड निष्पादित नहीं होगा।
गार्ड स्टेटमेंट के बारे में असली साफ बात यह है कि अब कोड में उपयोग करने के लिए अलिखित मान उपलब्ध है जो कथन का अनुसरण करता है (जैसा कि हम जानते हैं कि भविष्य का कोड केवल तभी निष्पादित हो सकता है जब वैकल्पिक का मान हो)। बयानों के अनुसार, कई घोंसले के शिकार द्वारा बनाए गए 'कयामत के पिरामिड' को खत्म करने के लिए यह बहुत अच्छा है ।
उदाहरण के लिए:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: \(number)!")
गार्ड भी उन्हीं साफ-सुथरी चालों का समर्थन करते हैं जो यदि कथन का समर्थन करती हैं, जैसे एक ही समय में कई वैकल्पिकों को खोलना और where
क्लॉज का उपयोग करना ।
चाहे आप एक या गार्ड स्टेटमेंट का पूरी तरह से उपयोग करते हैं, इस बात पर निर्भर करता है कि भविष्य में किसी कोड को एक मान शामिल करने के लिए वैकल्पिक की आवश्यकता है या नहीं ।
निल कॉलेशिंग ऑपरेटर
शून्य वालों ऑपरेटर के एक गंधा आशुलिपि संस्करण है त्रिगुट सशर्त ऑपरेटर , मुख्य रूप से गैर optionals को optionals कन्वर्ट करने के लिए बनाया गया है। इसका सिंटैक्स है a ?? b
, जहां a
एक वैकल्पिक प्रकार है और b
यह उसी प्रकार है a
(हालांकि आमतौर पर गैर-वैकल्पिक)।
यह अनिवार्य रूप से आपको कहने देता है “यदि a
इसमें कोई मूल्य है, तो इसे रद्द करें। अगर इसके बाद वापस नहीं जाता है तो b
"। उदाहरण के लिए, आप इसे इस तरह उपयोग कर सकते हैं:
let number = anOptionalInt ?? 0
यह एक number
निरंतर Int
प्रकार को परिभाषित करेगा , जिसमें या तो मान शामिल होगा anOptionalInt
, यदि इसमें कोई मान है, या 0
अन्यथा।
इसके लिए बस शॉर्टहैंड है:
let number = anOptionalInt != nil ? anOptionalInt! : 0
वैकल्पिक चेनिंग
आप वैकल्पिक कॉलिंग का उपयोग कर सकते हैं ताकि किसी विधि पर कॉल किया जा सके या वैकल्पिक पर किसी संपत्ति का उपयोग किया जा सके। यह बस इसका ?
उपयोग करते समय चर नाम के साथ प्रत्यय लगाकर किया जाता है।
उदाहरण के लिए, मान लें कि हमारे पास एक चर है foo
, एक वैकल्पिक Foo
उदाहरण।
var foo : Foo?
अगर हम foo
उस तरीके को कॉल करना चाहते हैं जो कुछ भी वापस नहीं करता है, तो हम बस कर सकते हैं:
foo?.doSomethingInteresting()
यदि foo
कोई मान है, तो इस पद्धति को उस पर कॉल किया जाएगा। यदि ऐसा नहीं होता है, तो कुछ भी बुरा नहीं होगा - कोड बस निष्पादित करना जारी रखेगा।
(यह nil
उद्देश्य-सी में संदेश भेजने के लिए समान व्यवहार है)
इसलिए इसका उपयोग गुणों को सेट करने के साथ-साथ कॉल विधियों के लिए भी किया जा सकता है। उदाहरण के लिए:
foo?.bar = Bar()
फिर, यहाँ कुछ भी बुरा नहीं होगा अगर foo
है nil
। आपका कोड बस क्रियान्वित होता रहेगा।
एक और साफ-सुथरी ट्रिक जो आपको वैकल्पिक चैनिंग करने देती है, यह जाँचती है कि कोई प्रॉपर्टी सेट करना या कोई विधि कॉल करना सफल था। आप रिटर्न मान की तुलना करके ऐसा कर सकते हैं nil
।
(ऐसा इसलिए है क्योंकि एक वैकल्पिक मूल्य एक विधि के Void?
बजाय वापस आ जाएगा Void
जो कुछ भी वापस नहीं करता है)
उदाहरण के लिए:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
हालांकि, जब संपत्तियों या कॉल के तरीकों का उपयोग करने की कोशिश की जाती है तो चीजें थोड़ी अधिक मुश्किल हो जाती हैं। क्योंकि foo
वैकल्पिक है, इसमें से दी गई कोई भी चीज़ वैकल्पिक होगी। इससे निपटने के लिए, आप या तो उन वैकल्पिक तरीकों को खोल सकते हैं जो उपरोक्त विधियों में से किसी एक का उपयोग करके वापस आ जाते हैं - या foo
मानों को वापस करने के तरीकों या कॉलिंग विधियों तक पहुँचने से पहले खुद को अनचाहे ।
इसके अलावा, जैसा कि नाम से पता चलता है, आप इन कथनों को एक साथ 'चेन' कर सकते हैं। इसका मतलब है कि अगर foo
कोई वैकल्पिक संपत्ति है baz
, जिसमें एक संपत्ति है qux
- आप निम्नलिखित लिख सकते हैं:
let optionalQux = foo?.baz?.qux
फिर से, क्योंकि foo
और baz
वैकल्पिक हैं, से लौटाया गया मूल्य qux
हमेशा एक वैकल्पिक होगा चाहे वह qux
खुद वैकल्पिक हो।
map
तथा flatMap
वैकल्पिक के साथ अक्सर उपयोग की जाने वाली सुविधा कार्य map
और flatMap
कार्यों का उपयोग करने की क्षमता है । ये आपको वैकल्पिक वैरिएबल में गैर-वैकल्पिक परिवर्तन लागू करने की अनुमति देते हैं। यदि किसी वैकल्पिक का मान है, तो आप इसमें दिए गए परिवर्तन को लागू कर सकते हैं। यदि इसका कोई मूल्य नहीं है, तो यह बना रहेगा nil
।
उदाहरण के लिए, मान लें कि आपके पास एक वैकल्पिक स्ट्रिंग है:
let anOptionalString:String?
map
फ़ंक्शन को उस पर लागू करने से - हम stringByAppendingString
फ़ंक्शन का उपयोग करके इसे किसी अन्य स्ट्रिंग तक पहुंचाने के लिए कर सकते हैं ।
क्योंकि stringByAppendingString
एक गैर-वैकल्पिक स्ट्रिंग तर्क लेता है, हम अपने वैकल्पिक स्ट्रिंग को सीधे इनपुट नहीं कर सकते। हालाँकि, उपयोग करके map
, हम अनुमति दे सकते हैं कि stringByAppendingString
यदि anOptionalString
कोई मान है तो इसका उपयोग किया जा सकता है ।
उदाहरण के लिए:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
हालाँकि, यदि anOptionalString
कोई मान नहीं है , तो map
वापस आ जाएगा nil
। उदाहरण के लिए:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
इसी तरह से काम करता है map
, सिवाय इसके कि आप बंद शरीर के भीतर से एक और वैकल्पिक वापस करने की अनुमति देता है। इसका मतलब है कि आप एक ऐसी प्रक्रिया में एक वैकल्पिक इनपुट कर सकते हैं जिसके लिए एक गैर-वैकल्पिक इनपुट की आवश्यकता होती है, लेकिन एक वैकल्पिक आउटपुट का उत्पादन कर सकते हैं।
try!
स्विफ्ट के एरर हैंडलिंग सिस्टम को सुरक्षित रूप से Do-Try-Catch के साथ प्रयोग किया जा सकता है :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
यदि someThrowingFunc()
कोई त्रुटि करता है, तो त्रुटि को catch
ब्लॉक में सुरक्षित रूप से पकड़ा जाएगा ।
error
लगातार आप में देख catch
ब्लॉक हमारे द्वारा घोषित नहीं किया गया है - यह स्वचालित रूप से द्वारा उत्पन्न कर रहा है catch
।
आप खुद को घोषित कर सकते हैं error
, इसका फायदा यह है कि इसे एक उपयोगी प्रारूप में डालने में सक्षम है, उदाहरण के लिए:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
try
इस तरह का उपयोग करना फेंकने के कार्यों से आने वाली त्रुटियों को आज़माने, पकड़ने और संभालने का उचित तरीका है।
वहाँ भी है try?
जो त्रुटि को अवशोषित करता है:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there's no error information available
}
लेकिन स्विफ्ट की त्रुटि से निपटने की प्रणाली भी इसके साथ "बल प्रयास" करने का एक तरीका प्रदान करती है try!
:
let result = try! someThrowingFunc()
इस पोस्ट में बताई गई अवधारणाएं यहां भी लागू होती हैं: यदि कोई त्रुटि होती है, तो एप्लिकेशन क्रैश हो जाएगा।
आपको केवल कभी-कभी उपयोग करना चाहिए try!
यदि आप यह साबित कर सकते हैं कि इसका परिणाम आपके संदर्भ में कभी भी विफल नहीं होगा - और यह बहुत दुर्लभ है।
अधिकांश समय आप पूर्ण Do-Try-Catch प्रणाली का उपयोग करेंगे - और वैकल्पिक एक, try?
दुर्लभ मामलों में जहां त्रुटि को संभालना महत्वपूर्ण नहीं है।
साधन