उद्देश्य-सी / कोको में अपवाद फेंकने का सबसे अच्छा तरीका क्या है?
उद्देश्य-सी / कोको में अपवाद फेंकने का सबसे अच्छा तरीका क्या है?
जवाबों:
मैं [NSException raise:format:]
निम्नानुसार उपयोग करता हूं :
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
यहाँ सावधानी का एक शब्द। उद्देश्य-सी में, कई समान भाषाओं के विपरीत, आपको आम तौर पर सामान्य त्रुटि स्थितियों के लिए अपवादों के उपयोग से बचने का प्रयास करना चाहिए जो सामान्य ऑपरेशन में हो सकते हैं।
ओबज-सी 2.0 के लिए एप्पल का प्रलेखन निम्नलिखित बताता है: "महत्वपूर्ण: अपवाद उद्देश्य-सी में संसाधन-गहन हैं। आपको सामान्य प्रवाह-नियंत्रण के लिए अपवादों का उपयोग नहीं करना चाहिए, या बस त्रुटियों को इंगित करना चाहिए (जैसे कि एक फ़ाइल सुलभ नहीं है)"
Apple के वैचारिक अपवाद से निपटने के दस्तावेज वही बताते हैं, लेकिन अधिक शब्दों के साथ: "महत्वपूर्ण: आपको प्रोग्रामिंग या अप्रत्याशित रनटाइम त्रुटियों के लिए अपवादों के उपयोग को आरक्षित करना चाहिए जैसे कि आउट-ऑफ-बाउंड्स संग्रह पहुंच, अपरिवर्तनीय वस्तुओं को म्यूट करने का प्रयास, एक अवैध संदेश भेजना , और विंडो सर्वर से कनेक्शन खोने पर। आप आमतौर पर अपवादों के साथ इस प्रकार की त्रुटियों का ध्यान रखते हैं जब कोई एप्लिकेशन रनटाइम के बजाय बनाया जा रहा होता है। [.....] अपवादों के बजाय, त्रुटि ऑब्जेक्ट्स (NSError) और कोकोआ त्रुटि-वितरण तंत्र कोकोआ अनुप्रयोगों में अपेक्षित त्रुटियों को संवाद करने का अनुशंसित तरीका है। ”
इसका कारण आंशिक रूप से ऑब्जेक्टिव-सी में प्रोग्रामिंग मुहावरों का पालन करना है (अधिक जटिल मामलों में साधारण मामलों में रिटर्न मानों और उप-संदर्भ मापदंडों (अक्सर NSError वर्ग) का उपयोग करके), आंशिक रूप से अपवादों को फेंकना और पकड़ना बहुत अधिक महंगा है और अंत में (और सबसे महत्वपूर्ण बात) कि ऑब्जेक्टिव-सी अपवाद C के सेटजम्प () और लॉन्गजम्प () फंक्शन्स के आसपास एक पतला आवरण है, जो अनिवार्य रूप से आपकी सावधान मेमोरी हैंडलिंग को गड़बड़ कर रहा है, इस स्पष्टीकरण को देखें ।
@throw([NSException exceptionWith…])
Xcode @throw
स्टेटमेंट्स को फंक्शन एग्जिट पॉइंट्स की तरह return
स्टेटमेंट्स की पहचान करता है। @throw
वाक्यविन्यास का उपयोग करने से ग़लतफ़हमी से बचा जाता है " नियंत्रण गैर-शून्य फ़ंक्शन के अंत तक पहुंच सकता है " चेतावनी जो आपको मिल सकती है [NSException raise:…]
।
इसके अलावा, @throw
उन वस्तुओं को फेंकने के लिए इस्तेमाल किया जा सकता है जो कक्षा NSException के नहीं हैं।
के संबंध में [NSException raise:format:]
। जावा बैकग्राउंड से आने वालों के लिए, आपको याद होगा कि जावा एक्सेप्शन और रनटाइम एक्सेप्शन के बीच अंतर करता है। अपवाद एक जाँच अपवाद है, और RuntimeException अनियंत्रित है। विशेष रूप से, जावा "सामान्य त्रुटि स्थितियों" के लिए जाँच किए गए अपवादों का उपयोग करने का सुझाव देता है और "क्रमादेशित त्रुटि के कारण रनटाइम त्रुटियों" के लिए अनियंत्रित अपवादों का उपयोग करता है। ऐसा लगता है कि ऑब्जेक्टिव-सी अपवादों का उपयोग उन्हीं स्थानों पर किया जाना चाहिए जहां आप अनियंत्रित अपवाद का उपयोग करेंगे, और त्रुटि कोड रिटर्न मान या NSError मान उन जगहों पर पसंद किए जाते हैं जहां आप एक चेक किए गए अपवाद का उपयोग करेंगे।
मुझे लगता है कि यह आपके अपने वर्ग के साथ Nthxception का विस्तार करने वाले @throw का उपयोग करने के लिए सुसंगत है। तो फिर तुम अंततः पकड़ने की कोशिश के लिए एक ही अधिसूचना का उपयोग करें:
@try {
.....
}
@catch{
...
}
@finally{
...
}
ऐप्पल ने यहां बताया कि अपवादों को कैसे फेंकना और संभालना है: कैचिंग एक्सेप्शन
ObjC 2.0 के बाद से, Objective-C अपवाद अब C के setjmp () longjmp () के लिए एक आवरण नहीं हैं, और C ++ अपवाद के साथ संगत हैं, @try "निःशुल्क है", लेकिन अपवादों को फेंकना और पकड़ना अधिक महंगा है।
वैसे भी, अभिकथन (NSAssert और NSCAssert मैक्रो परिवार का उपयोग करके) NSException को फेंक देते हैं, और उस संत को रीस राज्यों के रूप में उपयोग करने के लिए।
अपवादों के बजाय विफलताओं का संचार करने के लिए NSError का उपयोग करें।
NSError के बारे में त्वरित बिंदु:
NSError C स्टाइल त्रुटि कोड (पूर्णांक) के लिए मूल कारण को स्पष्ट रूप से पहचानने की अनुमति देता है और त्रुटि हैंडलर को त्रुटि को दूर करने की अनुमति देता है। आप NSError उदाहरणों में SQLite जैसे C पुस्तकालयों से त्रुटि कोड बहुत आसानी से लपेट सकते हैं।
NSError में एक ऑब्जेक्ट होने का लाभ भी है और इसके userInfo डिक्शनरी के सदस्य के साथ अधिक विस्तार से त्रुटि का वर्णन करने का एक तरीका प्रदान करता है।
लेकिन सभी के सर्वश्रेष्ठ, NSError CANNOT को फेंका नहीं जा सकता है, इसलिए यह अन्य भाषाओं के विपरीत त्रुटि से निपटने के लिए एक अधिक सक्रिय दृष्टिकोण को प्रोत्साहित करता है, जो केवल गर्म आलू को आगे और आगे कॉल स्टैक को फेंकते हैं जिस बिंदु पर यह केवल उपयोगकर्ता और उपयोगकर्ता को सूचित किया जा सकता है किसी भी सार्थक तरीके से नहीं संभाला जाता है (यदि आप OOP सूचना के सबसे बड़े सिद्धांत को छिपाने में विश्वास नहीं करते हैं)।
संदर्भ लिंक: संदर्भ
इसे मैंने "द बिग नर्ड रेंच गाइड (4 वें संस्करण)" से सीखा है।
@throw [NSException exceptionWithName:@"Something is not right exception"
reason:@"Can't perform this operation because of this or that"
userInfo:nil];
userInfo:nil
। :)
मेरा मानना है कि आपको सामान्य कार्यक्रम प्रवाह को नियंत्रित करने के लिए अपवादों का उपयोग कभी नहीं करना चाहिए। जब भी कुछ मूल्य वांछित मूल्य से मेल नहीं खाते हैं, लेकिन अपवादों को फेंक दिया जाना चाहिए।
उदाहरण के लिए यदि कोई फ़ंक्शन मान को स्वीकार करता है, और उस मान को कभी शून्य होने की अनुमति नहीं है, तो यह अपवाद को फेंकने के लिए ठीक है, फिर कुछ 'स्मार्ट' करने की कोशिश कर रहा है ...
आरआईई
आपको केवल अपवादों को फेंकना चाहिए यदि आप खुद को ऐसी स्थिति में पाते हैं जो प्रोग्रामिंग त्रुटि को इंगित करता है, और एप्लिकेशन को चलने से रोकना चाहता है। इसलिए, अपवाद फेंकने का सबसे अच्छा तरीका NSAssert और NSParameterAssert मैक्रोज़ का उपयोग कर रहा है, और यह सुनिश्चित करना है कि NS_BLOCK_ASSERTIONS परिभाषित नहीं है।
मामले के लिए नमूना कोड: @throw ([NSException अपवादWithName: ...
- (void)parseError:(NSError *)error
completionBlock:(void (^)(NSString *error))completionBlock {
NSString *resultString = [NSString new];
@try {
NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];
if(!errorData.bytes) {
@throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
}
NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
options:NSJSONReadingAllowFragments
error:&error];
resultString = dictFromData[@"someKey"];
...
} @catch (NSException *exception) {
NSLog( @"Caught Exception Name: %@", exception.name);
NSLog( @"Caught Exception Reason: %@", exception.reason );
resultString = exception.reason;
} @finally {
completionBlock(resultString);
}
}
का उपयोग करते हुए:
[self parseError:error completionBlock:^(NSString *error) {
NSLog(@"%@", error);
}];
एक और अधिक उन्नत उपयोग-मामला:
- (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock {
NSString *resultString = [NSString new];
NSException* customNilException = [NSException exceptionWithName:@"NilException"
reason:@"object is nil"
userInfo:nil];
NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException"
reason:@"object is not a NSNumber"
userInfo:nil];
@try {
NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]];
if(!errorData.bytes) {
@throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]);
}
NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData
options:NSJSONReadingAllowFragments
error:&error];
NSArray * array = dictFromData[@"someArrayKey"];
for (NSInteger i=0; i < array.count; i++) {
id resultString = array[i];
if (![resultString isKindOfClass:NSNumber.class]) {
[customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException;
break;
} else if (!resultString){
@throw customNilException; // <======
break;
}
}
} @catch (SomeCustomException * sce) {
// most specific type
// handle exception ce
//...
} @catch (CustomException * ce) {
// most specific type
// handle exception ce
//...
} @catch (NSException *exception) {
// less specific type
// do whatever recovery is necessary at his level
//...
// rethrow the exception so it's handled at a higher level
@throw (SomeCustomException * customException);
} @finally {
// perform tasks necessary whether exception occurred or not
}
}
सामान्य रूप से उद्देश्य सी में अपवादों का उपयोग नहीं करने का कोई कारण नहीं है यहां तक कि व्यावसायिक नियम अपवादों को भी इंगित करने के लिए। कह सकते हैं कि Apple NSError का उपयोग करता है जो परवाह करता है। ओबज सी एक लंबे समय के आसपास रहा है और एक समय में सभी सी ++ प्रलेखन ने एक ही बात कही है। इसका कारण यह नहीं है कि एक अपवाद को फेंकना और पकड़ना कितना महंगा है, क्या एक अपवाद का जीवनकाल बहुत कम है और ... इसका सामान्य प्रवाह के लिए एक अपवाद है। मैंने अपने जीवन में कभी किसी को यह कहते हुए नहीं सुना, वह आदमी जिसे फेंकने और पकड़ने में लंबा समय लगा।
इसके अलावा, ऐसे लोग हैं जो सोचते हैं कि उद्देश्य C स्वयं बहुत महंगा है और इसके बजाय C या C ++ में कोड है। इसलिए कहा जाता है कि हमेशा NSError का उपयोग करना गलत जानकारी और अपवित्र है।
लेकिन इस थ्रेड के सवाल का अभी तक उत्तर नहीं मिला है कि अपवाद को फेंकने का सबसे अच्छा तरीका क्या है। NSError को वापस करने के तरीके स्पष्ट हैं।
तो यह है: [NSException बढ़ाएँ: ... @throw [[NSException आवंटन] initWithName .... या @throw [[MyCustomException ...?
मैं यहाँ जाँच / अनियंत्रित नियम का उपयोग ऊपर से थोड़ा अलग करता हूँ।
(जावा रूपक का उपयोग करके) यहाँ जाँच / अनियंत्रित के बीच वास्तविक अंतर महत्वपूर्ण है -> क्या आप अपवाद से उबर सकते हैं। और ठीक होने से मेरा मतलब है कि सिर्फ दुर्घटना नहीं।
इसलिए मैं पुनर्प्राप्त अपवादों के लिए @throw के साथ कस्टम अपवाद कक्षाओं का उपयोग करता हूं, क्योंकि इसकी संभावना है कि मेरे पास कुछ ऐप विधि कई @ ब्लॉक ब्लॉक में कुछ प्रकार की विफलताओं की तलाश होगी। उदाहरण के लिए यदि मेरा ऐप एक एटीएम मशीन है, तो मेरे पास "WithdrawalRequestExceedsBalanceException" के लिए एक @catch ब्लॉक होगा।
मैं NSException का उपयोग करता हूं: रनटाइम अपवादों के लिए उठाता हूं क्योंकि मेरे पास अपवाद से पुनर्प्राप्त करने का कोई तरीका नहीं है, सिवाय इसके कि इसे उच्च स्तर पर पकड़ा जाए और लॉग इन किया जाए। और उस के लिए एक कस्टम वर्ग बनाने में कोई मतलब नहीं है।
वैसे भी मैं जो कुछ भी करता हूं, लेकिन अगर बेहतर, समान रूप से अभिव्यंजक तरीका है, तो मैं भी जानना चाहता हूं। अपने स्वयं के कोड में, जब से मैंने सी को एक हेला कोडिंग को बंद कर दिया था, तब से मैं एक एनएसईआरआर को कभी वापस नहीं करता, भले ही मुझे एक एपीआई द्वारा पारित किया गया हो।
@throw([NSException exceptionWith…])
क्योंकि इसे अधिक संक्षिप्त रूप में दृष्टिकोण के लिए अपील किया गया है ।