iPhone 앱에서 NSError를 어떻게 사용합니까?


228

내 앱에서 오류를 잡기 위해 노력하고 있으며을 사용하고 NSError있습니다. 사용 방법과 채우기 방법에 대해 약간 혼란 스럽습니다.

누군가 내가 어떻게 채우고 사용하는지에 대한 예를 제공 할 수 NSError있습니까?

답변:


473

글쎄, 일반적으로 런타임에 오류가 발생할 수있는 메소드가 NSError포인터를 참조하는 것 입니다. 해당 메소드에서 실제로 문제 NSError가 발생하면 오류 데이터로 참조를 채우고 메소드에서 nil을 반환 할 수 있습니다 .

예:

- (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;
}

그런 다음 이와 같은 방법을 사용할 수 있습니다. 메소드가 nil을 반환하지 않으면 오류 객체를 검사하지 않아도됩니다.

// 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의 문서 입니다. 정말 좋습니다.

Cocoa Is My Girlfriend 에 대한 훌륭하고 간단한 자습서도 있습니다 .


37
이것은 가장 재미있는 예입니다
ming yeow

이 ARC에서 몇 가지 문제가 있으며 주조 있지만 이것은 아주 멋진 대답 idA를 BOOL. 약간의 ARC 호환 변형이 많이 인정 될 것입니다.
NSTJ

6
@TomJowett 애플이 우리를 새로운 ARC 전용 세계로 옮기도록 강요했기 때문에 세계 기아를 종식시킬 수 없게되면 정말 화가 났을 것입니다.
Manav

1
반환 유형은입니다 BOOL. NO오류가 발생한 경우 반환 하고 반환 값을 확인하는 대신 확인하십시오 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) 정수 코드를 지역화 된 오류 메시지로 변환하는 매크로를 추가하십시오. Constants + Macros.h 파일에 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 userInfo : @ {NSLocalizedDescriptionKey : FS_ERROR_LOCALIZED_DESCRIPTION (error.code)}];
Richard Venable 14

1
문자열 파일을 넣을 특정 장소가 있습니까? FS_ERROR_LOCALIZED_DESCRIPTION ()에서 숫자 (오류 코드) 만 얻습니다.
huggie

@ huggie : 당신이 무슨 뜻인지 확실하지 않습니다. 필자는 일반적으로 전체 앱에서 사용하는 이러한 매크로를 파일이라는 Constants+Macros.h파일에 넣고이 파일을 접두사 헤더 ( .pch파일)로 가져와 어디서나 사용할 수 있습니다. 2 개의 매크로 중 하나만 사용하고 있다면 효과가있을 수 있습니다. 아마도 이것을 테스트하지는 않았지만로 변환 int하는 NSString것이 실제로 필요하지는 않습니다.
Wolfgang Schreurs

@ huggie :와, 나는 지금 당신을 이해하는 것 같아요. 문자열은 지역화 가능한 파일 ( .strings파일) 에 있어야합니다 . Apple의 매크로가 표시되는 위치이기 때문입니다. 사용에 대해 읽으 NSLocalizedStringFromTable십시오 : developer.apple.com/library/mac/documentation/cocoa/conceptual/…
Wolfgang Schreurs

1
@ huggie : 예, 현지화 된 문자열 테이블을 사용했습니다. 매크로의 코드는 FS_ERROR_LOCALIZED_DESCRIPTION라는 파일에서 지역화 가능한 문자열을 확인합니다 FSError.strings. .strings외국어 파일 인 경우 Apple의 현지화 안내서를 확인하십시오 .
Wolfgang Schreurs

38

좋은 대답 알렉스. 잠재적 인 문제 중 하나는 NULL 역 참조입니다. 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

목표 -C

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

난 당신이 반환해야하기 때문에 ARC 불평 때문에 지금까지가 아니라 (모든 ARC 호환되도록하는 수정 추가, 위대한 알렉스 의해 답변과 jlmendezbonini의 관점을 요약 해보자 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여전히 is 인지 확인합니다 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
@Gabriela : Apple은 간접 변수를 사용하여 오류를 반환 할 때 성공 또는 실패시 메소드 자체에 항상 반환 값이 있어야한다고 말합니다. Apple은 개발자에게 먼저 반환 값을 확인하고 반환 값이 어떤 방식 으로든 잘못된 오류 확인 인 경우 에만 촉구 합니다. 다음 페이지를 참조하십시오 : developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/...
볼프강가 Schreurs

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.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.