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

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

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



답변

글쎄, 일반적으로 런타임에 오류가 발생할 수있는 메소드가 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 에 대한 훌륭하고 간단한 자습서도 있습니다 .


답변

가장 최근의 구현을 기반으로 몇 가지 제안을 추가하고 싶습니다. 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];


답변

좋은 대답 알렉스. 잠재적 인 문제 중 하나는 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;
...


답변

목표 -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"])


답변

다음 튜토리얼을 참조하십시오

나는 그것이 당신에게 도움이되기를 희망하지만, 당신은 NSError의 문서를 읽기 전에

이것은 내가 최근에 발견 매우 흥미로운 링크입니다 에러 처리


답변

난 당신이 반환해야하기 때문에 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.


답변

내가 본 또 다른 디자인 패턴은 블록을 사용하는 것과 관련이 있는데, 이는 메소드가 비동기 적으로 실행될 때 특히 유용합니다.

다음과 같은 오류 코드가 정의되어 있다고 가정하십시오.

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");
    }
}];