मैं अपने iPhone ऐप में NSError का उपयोग कैसे कर सकता हूं?


228

मैं अपने ऐप में त्रुटियों को पकड़ने पर काम कर रहा हूं, और मैं उपयोग में देख रहा हूं NSError। मैं इसे कैसे उपयोग करना है, और इसे कैसे आबाद करना है, इस बारे में थोड़ा उलझन में हूं।

क्या कोई उदाहरण दे सकता है कि मैं कैसे उपयोग करता हूं NSError?

जवाबों:


473

खैर, जो मैं आमतौर पर करता हूं वह मेरे तरीके हैं जो रनटाइम के समय त्रुटि-आउट एक NSErrorपॉइंटर का संदर्भ ले सकते हैं । अगर वास्तव में उस पद्धति में कुछ गलत होता है, तो मैं NSErrorत्रुटि डेटा के साथ संदर्भ को पॉप्युलेट कर सकता हूं और विधि से शून्य वापस कर सकता हूं ।

उदाहरण:

- (id) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
    // begin feeding the world's children...
    // it's all going well until....
    if (ohNoImOutOfMonies) {
        // sad, we can't solve world hunger, but we can let people know what went wrong!
        // init dictionary to be used to populate error object
        NSMutableDictionary* details = [NSMutableDictionary dictionary];
        [details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];
        // populate the error object with the details
        *error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
        // we couldn't feed the world's children...return nil..sniffle...sniffle
        return nil;
    }
    // wohoo! We fed the world's children. The world is now in lots of debt. But who cares? 
    return YES;
}

हम फिर इस तरह की विधि का उपयोग कर सकते हैं। जब तक यह विधि शून्य न हो जाए, तब तक त्रुटि ऑब्जेक्ट का निरीक्षण करने की जहमत न उठायें:

// initialize NSError object
NSError* error = nil;
// try to feed the world
id yayOrNay = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!yayOrNay) {
   // inspect error
   NSLog(@"%@", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.

हम त्रुटि का उपयोग करने में सक्षम थे localizedDescriptionक्योंकि हम इसके लिए एक मूल्य निर्धारित करते हैं NSLocalizedDescriptionKey

अधिक जानकारी के लिए सबसे अच्छी जगह Apple के प्रलेखन है । यह वास्तव में अच्छा है।

कोको इज़ माय गर्लफ्रेंड पर एक अच्छा, सरल ट्यूटोरियल भी है ।


37
यह सबसे मजेदार उदाहरण है, कभी
यिंग

हालांकि वहां एआरसी में कुछ मुद्दे हैं और कास्टिंग यह एक बहुत भयानक जवाब है, idएक करने के लिए BOOL। किसी भी मामूली एआरसी संगत भिन्नता की बहुत सराहना की जाएगी।
NSTJ

6
@TomJowett अगर हम अंत में दुनिया की भूख को समाप्त करने में सक्षम नहीं हो पा रहे हैं तो मैं वास्तव में नाराज हो जाऊंगा क्योंकि Apple ने हमें नए ARC दुनिया में जाने के लिए प्रेरित किया।
मानव

1
वापसी प्रकार किया जा सकता है BOOLNOत्रुटि के मामले में वापसी और वापसी मूल्य के लिए जाँच करने के बजाय, बस जाँच करें error। अगर nilआगे बढ़ें, तो != nilसंभलें।
गेब्रियल पेट्रोनेला

8
-1: आपको वास्तव में उस कोड को शामिल करने की आवश्यकता है जो सत्यापन **errorशून्य नहीं है। अन्यथा कार्यक्रम एक त्रुटि फेंक देगा जो पूरी तरह से अमित्र है और यह स्पष्ट नहीं करता है कि क्या हो रहा है।
FreeAsInBeer

58

मैं अपने सबसे हालिया कार्यान्वयन के आधार पर कुछ और सुझाव जोड़ना चाहूंगा। मैंने Apple के कुछ कोड को देखा है और मुझे लगता है कि मेरा कोड उसी तरह से व्यवहार करता है।

ऊपर दिए गए पोस्ट पहले ही बता देते हैं कि NSError वस्तुओं को कैसे बनाया जाता है और उन्हें कैसे वापस किया जाता है, इसलिए मैं उस हिस्से से परेशान नहीं होऊंगा। मैं सिर्फ आपके ऐप में त्रुटियों (कोड, संदेश) को एकीकृत करने का एक अच्छा तरीका सुझाने की कोशिश करूंगा।


मैं 1 हेडर बनाने की सलाह देता हूं जो आपके डोमेन (यानी ऐप, लाइब्रेरी, आदि ..) की सभी त्रुटियों का अवलोकन होगा। मेरा वर्तमान हेडर इस तरह दिखता है:

FSError.h

FOUNDATION_EXPORT NSString *const FSMyAppErrorDomain;

enum {
    FSUserNotLoggedInError = 1000,
    FSUserLogoutFailedError,
    FSProfileParsingFailedError,
    FSProfileBadLoginError,
    FSFNIDParsingFailedError,
};

FSError.m

#import "FSError.h" 

NSString *const FSMyAppErrorDomain = @"com.felis.myapp";

अब त्रुटियों के लिए उपरोक्त मूल्यों का उपयोग करते समय, Apple आपके ऐप के लिए कुछ बुनियादी मानक त्रुटि संदेश बनाएगा। निम्नलिखित की तरह एक त्रुटि बनाई जा सकती है:

+ (FSProfileInfo *)profileInfoWithData:(NSData *)data error:(NSError **)error
{
    FSProfileInfo *profileInfo = [[FSProfileInfo alloc] init];
    if (profileInfo)
    {
        /* ... lots of parsing code here ... */

        if (profileInfo.username == nil)
        {
            *error = [NSError errorWithDomain:FSMyAppErrorDomain code:FSProfileParsingFailedError userInfo:nil];            
            return nil;
        }
    }
    return profileInfo;
}

error.localizedDescriptionउपरोक्त कोड के लिए मानक Apple-जनित त्रुटि संदेश ( ) निम्नलिखित की तरह दिखाई देगा:

Error Domain=com.felis.myapp Code=1002 "The operation couldn’t be completed. (com.felis.myapp error 1002.)"

ऊपर एक डेवलपर के लिए पहले से ही काफी मददगार है, क्योंकि संदेश उस डोमेन को प्रदर्शित करता है जहां त्रुटि हुई थी और संबंधित त्रुटि कोड। अंतिम उपयोगकर्ताओं के पास कोई त्रुटि नहीं होगी कि त्रुटि कोड 1002का क्या अर्थ है, इसलिए अब हमें प्रत्येक कोड के लिए कुछ अच्छे संदेशों को लागू करने की आवश्यकता है।

त्रुटि संदेशों के लिए हमें स्थानीयकरण को ध्यान में रखना होगा (भले ही हम स्थानीय संदेशों को तुरंत लागू न करें)। मैंने अपनी वर्तमान परियोजना में निम्नलिखित दृष्टिकोण का उपयोग किया है:


1) एक stringsफ़ाइल बनाएं जिसमें त्रुटियां होंगी। स्ट्रिंग्स फ़ाइलें आसानी से स्थानीय हैं। फ़ाइल निम्नलिखित की तरह दिख सकती है:

FSError.strings

"1000" = "User not logged in.";
"1001" = "Logout failed.";
"1002" = "Parser failed.";
"1003" = "Incorrect username or password.";
"1004" = "Failed to parse FNID."

2) स्थानीय त्रुटि संदेशों में पूर्णांक कोड बदलने के लिए मैक्रोज़ जोड़ें। मैंने अपनी स्थिरांक + मैक्रोज़.एच फ़ाइल में 2 मैक्रोज़ का उपयोग किया है। मैं हमेशा इस फ़ाइल को सुविधा के लिए उपसर्ग शीर्षक ( MyApp-Prefix.pch) में शामिल करता हूँ ।

स्थिरांक + Macros.h

// error handling ...

#define FS_ERROR_KEY(code)                    [NSString stringWithFormat:@"%d", code]
#define FS_ERROR_LOCALIZED_DESCRIPTION(code)  NSLocalizedStringFromTable(FS_ERROR_KEY(code), @"FSError", nil)

3) अब एक त्रुटि कोड के आधार पर उपयोगकर्ता के अनुकूल त्रुटि संदेश दिखाना आसान है। एक उदाहरण:

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" 
            message:FS_ERROR_LOCALIZED_DESCRIPTION(error.code) 
            delegate:nil 
            cancelButtonTitle:@"OK" 
            otherButtonTitles:nil];
[alert show];

9
बहुत बढ़िया जवाब! लेकिन उपयोगकर्ता जानकारी शब्दकोश में स्थानीयकृत विवरण को क्यों नहीं रखा गया है? [NSError errorWithDomain: FSMyAppErrorDomain कोड: FSProfileParsingFailedError उपयोगकर्ताइन्फो: @ {NSLocalizedDescriptionKey: FS_ERRR_LOCALIZED_DESCRIPTION (error.code)}];
रिचर्ड वेनेबल

1
क्या कोई विशेष स्थान है जहां मुझे स्ट्रिंग फ़ाइल डालनी चाहिए? FS_ERROR_LOCALIZED_DESCRIPTION () से मुझे केवल संख्या (त्रुटि कोड) मिल रही है।
हगी

@ ह्यूगी: वास्तव में निश्चित नहीं कि आपका क्या मतलब है। मैं आम तौर पर इन मैक्रो को लगाता हूं जिसका उपयोग मैं पूरे ऐप में करता हूं जिसे एक फाइल कहा जाता है Constants+Macros.hऔर इस फाइल को प्रीफिक्स हेडर ( .pchफाइल) में इम्पोर्ट करता है, इसलिए यह हर जगह उपलब्ध है। यदि आपका मतलब है कि आप केवल 2 मैक्रोज़ में से 1 का उपयोग कर रहे हैं, तो यह काम कर सकता है। शायद से रूपांतरण intकरने के लिए NSStringहालांकि मैं इस परीक्षण नहीं किया, वास्तव में आवश्यक नहीं है।
वोल्फगैंग श्रेयर्स

@ ह्यूगी: ओउ, मुझे लगता है कि मैं अब आपको समझ गया हूं। तार एक स्थानीय फ़ाइल ( .stringsफ़ाइल) में होना चाहिए , क्योंकि यही वह जगह है जहाँ से Apple का मैक्रो दिखेगा। NSLocalizedStringFromTableयहाँ उपयोग करने के बारे में पढ़ें : developer.apple.com/library/mac/documentation/cocoa/conceptual/…
वोल्फगैंग श्रेयर्स

1
@huggie: हाँ, मैंने स्थानीयकृत स्ट्रिंग तालिकाओं का उपयोग किया है। मैक्रो में कोड FS_ERROR_LOCALIZED_DESCRIPTIONएक फ़ाइल नामक स्थान पर स्थानीय स्ट्रिंग की जाँच करता है FSError.strings। यदि आप .stringsयह आपके लिए विदेशी हैं, तो आप फ़ाइलों पर Apple के स्थानीयकरण गाइड की जांच करना चाहते हैं।
वोल्फगैंग श्रेयर्स

38

महान जवाब एलेक्स। एक संभावित समस्या है NULL dereference। NSError ऑब्जेक्ट बनाने और वापस करने पर Apple का संदर्भ

...
[details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];

if (error != NULL) {
    // populate the error object with the details
    *error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
}
// we couldn't feed the world's children...return nil..sniffle...sniffle
return nil;
...

30

उद्देश्य सी

NSError *err = [NSError errorWithDomain:@"some_domain"
                                   code:100
                               userInfo:@{
                                           NSLocalizedDescriptionKey:@"Something went wrong"
                               }];

स्विफ्ट 3

let error = NSError(domain: "some_domain",
                      code: 100,
                  userInfo: [NSLocalizedDescriptionKey: "Something went wrong"])

9

कृपया निम्नलिखित ट्यूटोरियल देखें

मुझे आशा है कि यह आपके लिए उपयोगी होगा, लेकिन इससे पहले आपको NSError के प्रलेखन को पढ़ना होगा

यह बहुत ही रोचक लिंक है जो मैंने हाल ही में एररहैंडलिंग पाया


3

मैं एलेक्स और jlmendezbonini के शानदार जवाब को संक्षेप में बताने की कोशिश करूंगा, एक ऐसा संशोधन जो सब कुछ ARC को संगत बना देगा (अब तक ARC शिकायत नहीं करेगा क्योंकि आपको वापस लौट जाना चाहिए id, जिसका अर्थ है "कोई भी वस्तु", लेकिन BOOLएक वस्तु नहीं है प्रकार)।

- (BOOL) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
    // begin feeding the world's children...
    // it's all going well until....
    if (ohNoImOutOfMonies) {
        // sad, we can't solve world hunger, but we can let people know what went wrong!
        // init dictionary to be used to populate error object
        NSMutableDictionary* details = [NSMutableDictionary dictionary];
        [details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey];
        // populate the error object with the details
        if (error != NULL) {
             // populate the error object with the details
             *error = [NSError errorWithDomain:@"world" code:200 userInfo:details];
        }
        // we couldn't feed the world's children...return nil..sniffle...sniffle
        return NO;
    }
    // wohoo! We fed the world's children. The world is now in lots of debt. But who cares? 
    return YES;
}

अब हमारे विधि कॉल के वापसी मूल्य के लिए जाँच करने के बजाय, हम जाँचते हैं कि क्या errorअभी भी है nil। अगर यह हमारे पास समस्या नहीं है।

// initialize NSError object
NSError* error = nil;
// try to feed the world
BOOL success = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!success) {
   // inspect error
   NSLog(@"%@", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.

3
@ गैब्रिएला: ऐप्पल ने कहा है कि त्रुटियों को वापस करने के लिए अप्रत्यक्ष चर का उपयोग करते समय, सफलता या विफलता के मामले में विधि को हमेशा कुछ वापसी मूल्य होना चाहिए। Apple डेवलपर्स से सबसे पहले रिटर्न वैल्यू के लिए जाँच करने का आग्रह करता है और केवल तभी जब रिटर्न वैल्यू किसी तरह त्रुटियों के लिए अमान्य होती है। निम्नलिखित पृष्ठ देखें: developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
वोल्फगैंग श्रेयर्स

3

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

मान लें कि हमारे पास निम्नलिखित त्रुटि कोड हैं:

typedef NS_ENUM(NSInteger, MyErrorCodes) {
    MyErrorCodesEmptyString = 500,
    MyErrorCodesInvalidURL,
    MyErrorCodesUnableToReachHost,
};

आप अपनी पद्धति को परिभाषित करेंगे जो एक त्रुटि को बढ़ा सकती है जैसे:

- (void)getContentsOfURL:(NSString *)path success:(void(^)(NSString *html))success failure:(void(^)(NSError *error))failure {
    if (path.length == 0) {
        if (failure) {
            failure([NSError errorWithDomain:@"com.example" code:MyErrorCodesEmptyString userInfo:nil]);
        }
        return;
    }

    NSString *htmlContents = @"";

    // Exercise for the reader: get the contents at that URL or raise another error.

    if (success) {
        success(htmlContents);
    }
}

और फिर जब आप इसे कॉल करते हैं, तो आपको NSError ऑब्जेक्ट घोषित करने के बारे में चिंता करने की आवश्यकता नहीं है (कोड पूरा करना आपके लिए होगा), या रिटर्निंग मान की जांच करना। आप केवल दो ब्लॉकों की आपूर्ति कर सकते हैं: एक जो अपवाद होने पर कॉल किया जाएगा, और एक जिसे कॉल किया जाता है जब वह सफल होता है:

[self getContentsOfURL:@"http://google.com" success:^(NSString *html) {
    NSLog(@"Contents: %@", html);
} failure:^(NSError *error) {
    NSLog(@"Failed to get contents: %@", error);
    if (error.code == MyErrorCodesEmptyString) { // make sure to check the domain too
        NSLog(@"You must provide a non-empty string");
    }
}];

0

वैसे यह सवाल के दायरे से थोड़ा बाहर है लेकिन अगर आपके पास NSError का विकल्प नहीं है तो आप हमेशा निम्न स्तर की त्रुटि प्रदर्शित कर सकते हैं:

 NSLog(@"Error = %@ ",[NSString stringWithUTF8String:strerror(errno)]);

0
extension NSError {
    static func defaultError() -> NSError {
        return NSError(domain: "com.app.error.domain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Something went wrong."])
    }
}

NSError.defaultError()जब भी मैं मान्य त्रुटि ऑब्जेक्ट नहीं है मैं उपयोग कर सकते हैं ।

let error = NSError.defaultError()
print(error.localizedDescription) //Something went wrong.
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.