objective-c / cocoa에서 예외 발생


답변:


528

[NSException raise:format:]다음과 같이 사용 합니다.

[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];

9
@throw([NSException exceptionWith…])더 간결한 접근 방식에 따라이 방법을 선호합니다 .
Sam Soffes

9
위험으로부터 중요한 경고를 읽으십시오 ( stackoverflow.com/questions/324284/324805#324805 )
e.James

26
나는 일반적으로 이것을 선호하지만, 하나의 문제가 있습니다. Xcode의 현재 버전 일 수도 있지만 [NSException raise ...] 구문은 구문 분석기에서 값을 리턴하는 메소드의 종료 경로로 인식되지 않는 것 같습니다. 이 구문을 사용할 때 "제어가 무효가 아닌 함수의 끝에 도달 할 수 있습니다"라는 경고가 표시되지만 @throw ([NSException exceptionWith…]) 구문을 사용하면 구문 분석기가이를 종료로 인식하고 경고를 표시하지 않습니다.
mattorb

1
@mpstx 나는 항상 주어진 이유 때문에 throw 구문을 사용합니다 (2 년 후 Xcode 4.6에서는 여전히 관련이 있으며 아마도 항상있을 것입니다). IDE가 예외를 발생시키는 것이 함수 종료점임을 인식하게하면 경고를 피하려는 경우에 중요합니다.
마크 애 머리

FWIW @ try / @ catch 블록은 "제어가 비 공백 기능의 끝에 도달 함"경고에 대해 잘못된 부정을 초래한다는 사실에 주목하고 있습니다 (즉, 경고는 표시되어야 할 때 표시되지 않음)
Brian Gerstle

256

여기서주의 할 점. Objective-C에서는 많은 다른 언어와 달리 일반적으로 정상적인 작동에서 발생할 수있는 일반적인 오류 상황에 예외를 사용하지 않도록 노력해야합니다.

Obj-C 2.0에 대한 Apple의 설명서 는 다음과 같이 설명합니다. "중요 : Objective-C에서 예외는 리소스를 많이 사용합니다. 일반적인 흐름 제어에 예외를 사용하거나 단순히 오류 (예 : 파일에 액세스 할 수없는 파일)를 나타내서는 안됩니다."

Apple의 개념적인 예외 처리 문서 는 동일하지만 더 많은 단어로 설명합니다. "중요 : 범위를 벗어난 콜렉션 액세스, 변경 불가능한 오브젝트의 변경, 유효하지 않은 메시지 전송과 같은 예상치 않은 런타임 오류에 대한 예외 사용을 예약해야합니다. 창 서버와의 연결이 끊어짐 일반적으로 런타임이 아닌 응용 프로그램을 만들 때 예외를 제외하고 이러한 종류의 오류를 처리합니다 [.....] 예외 대신 오류 개체 (NSError) 및 Cocoa 오류 전달 메커니즘은 Cocoa 응용 프로그램에서 예상되는 오류를 전달하는 데 권장되는 방법입니다. "

이것의 이유는 부분적으로 예외를 던지고 잡는 것이 훨씬 비싸고 Objective-C의 프로그래밍 관용구 (간단한 경우에는 리턴 값을 사용하고 더 복잡한 경우에는 참조 매개 변수 (종종 NSError 클래스)를 사용함)를 고수하기 위해서입니다. 마지막으로 Objective-C 예외는 C의 setjmp () 및 longjmp () 함수 주위의 얇은 래퍼이며 본질적으로 신중한 메모리 처리를 망칠 수 있습니다. 만듭니다. 이 설명을 .


11
나는 이것이 대부분의 프로그래밍 언어에 적용된다고 생각한다 : "일반적인 오류 상황에서 예외 사용을 피하라". Java에서도 마찬가지입니다. 예외를 제외하고 사용자 입력 오류를 처리하는 것은 좋지 않습니다. 리소스 사용뿐만 아니라 코드 명확성 때문입니다.
beetstra

6
더 중요한 것은 Cocoa의 예외는 복구 할 수없는 프로그램 오류를 나타내도록 설계되었습니다. 그렇지 않으면 프레임 워크에 대해 실행되고 정의되지 않은 동작이 발생할 수 있습니다. 자세한 내용은 stackoverflow.com/questions/3378696/iphone-try-end-try/… 를 참조하십시오.
KPM

9
"자바에도 동일하게 적용됩니다." 동의하지 않는다. 정상적인 오류 조건에 대해서는 Java에서 확인 된 예외를 잘 사용할 수 있습니다. 물론 런타임 예외는 사용하지 않습니다.
Daniel Ryan

나는 코코아 방식을 선호합니다 (예외는 프로그래머 오류 만 제외) Java에서도 선호 하지만 실제로는 환경에서 일반적인 관행을 따라야하며 오류 처리에 대한 예외는 다음과 같이 나타납니다. Objective-C에는 나쁜 냄새가 있지만 Java에서는 그 목적으로 많이 사용됩니다.
gnasher729

1
이 의견은 질문에 답변하지 않습니다. OP는 충돌 보고서 프레임 워크가 예상대로 작동하는지 테스트하기 위해 앱을 중단하려고 할 수 있습니다.
Simon

62
@throw([NSException exceptionWith…])

Xcode는 @throw명령문과 같은 명령문을 함수 종료점으로 인식 return합니다. @throw구문을 사용 하면 잘못된 " 제어가 무효화 기능 끝에 도달 할 수 있습니다 "경고가 표시 되지 않습니다[NSException raise:…] .

또한 @throwNSException 클래스가 아닌 객체를 던지는 데 사용할 수 있습니다.


11
@Steph Thirion : 자세한 내용은 developer.apple.com/documentation/Cocoa/Conceptual/Exceptions/… 를 참조 하십시오. 결론? 둘 다 작동하지만 @throw를 사용하여 NSException 클래스가 아닌 객체를 던질 수 있습니다.
e.James

33

에 관해서 [NSException raise:format:]. Java 배경에서 온 사람들에게는 Java가 Exception과 RuntimeException을 구분한다는 것을 기억할 것입니다. 예외는 확인 된 예외이며 RuntimeException은 확인되지 않습니다. 특히, Java는 "정상 오류 조건"에 대해 확인 된 예외를 사용하고 "프로그래머 오류로 인한 런타임 오류"에 대해 확인되지 않은 예외를 사용할 것을 제안합니다. Objective-C 예외는 검사되지 않은 예외를 사용하는 것과 동일한 위치에서 사용해야하며, 검사 된 예외를 사용하는 경우 오류 코드 리턴 값 또는 NSError 값이 선호됩니다.


1
예, 이것이 정확합니다 (4 년 후 : D), NSError 클래스에서 확장되는 자체 오류 클래스 ABCError를 만들고 NSExceptions 대신 확인 된 예외에 사용하십시오. 프로그래머 오류 (예 : 숫자 형식 문제와 같은 예기치 않은 상황)가 발생하는 NSException을 발생시킵니다.
chathuram

15

NSException을 확장하는 자체 클래스와 함께 @throw를 사용하는 것이 더 좋다고 생각합니다. 그런 다음 try catch에 동일한 표기법을 사용하십시오.

@try {
.....
}
@catch{
...
}
@finally{
...
}

Apple은 예외를 처리하고 처리하는 방법을 여기에서 설명합니다. 예외 잡기 예외 던지기


난 여전히 try 블록에서 런타임 예외로 충돌 있어요
famfamfam

14

ObjC 2.0부터 Objective-C 예외는 더 이상 C의 setjmp () longjmp ()에 대한 래퍼가 아니며 C ++ 예외와 호환되므로 @try는 "무료"이지만 예외를 던지고 잡는 것이 훨씬 비쌉니다.

어쨌든 (NSAssert 및 NSCAssert 매크로 패밀리를 사용하는) 어설 션은 NSException을 발생시키고 그 상태를 Ries 상태로 사용합니다.


알아 둘만 한! 가장 작은 오류에 대해서도 예외를 발생시키는 수정하고 싶지 않은 타사 라이브러리가 있습니다. 우리는 앱에서 한곳에서 그것들을 잡아야 만하고 우리를 울부 짖게하지만, 이것은 조금 기분이 좋아집니다.
유리 Brigance

8

NSError를 사용하여 예외가 아닌 장애를 전달하십시오.

NSError에 대한 빠른 포인트 :

  • NSError를 사용하면 C 스타일 오류 코드 (정수)가 근본 원인을 명확하게 식별하고 오류 처리기가 오류를 극복 할 수 있습니다. NSError 인스턴스에서 SQLite와 같은 C 라이브러리의 오류 코드를 매우 쉽게 래핑 할 수 있습니다.

  • NSError는 또한 객체라는 이점을 가지고 있으며 userInfo 사전 멤버로 오류를보다 자세하게 설명 할 수있는 방법을 제공합니다.

  • 그러나 무엇보다도, NSError는 던져 질 수 없으므로 단순히 뜨거운 감자를 더 던지고 콜 스택을 위로 올리는 다른 언어와 달리 오류 처리에 대한 사전 대처 방식을 권장합니다. 의미있는 방식으로 처리되지 않습니다 (OOP의 가장 큰 정보 숨기고 있다고 믿는 경우는 아닙니다).

참조 링크 : 참조


이 의견은 질문에 답변하지 않습니다. OP는 충돌 보고서 프레임 워크가 예상대로 작동하는지 테스트하기 위해 앱을 중단하려고 할 수 있습니다.
Simon

7

이것이 "The Big Nerd Ranch Guide (제 4 판)"에서 배운 방법입니다.

@throw [NSException exceptionWithName:@"Something is not right exception"
                               reason:@"Can't perform this operation because of this or that"
                             userInfo:nil];

userInfo:nil좋습니다. 그러나에 대해서는별로 설명하지 않습니다 . :)
Cœur

6

try catch 블록에서 두 가지 방법으로 예외를 발생시킬 수 있습니다.

@throw[NSException exceptionWithName];

또는 두 번째 방법

NSException e;
[e raise];

3

정상적인 프로그램 흐름을 제어하기 위해 예외를 사용해서는 안된다고 생각합니다. 그러나 일부 값이 원하는 값과 일치하지 않을 때마다 예외가 발생해야합니다.

예를 들어 어떤 함수가 값을 받아들이고 그 값이 절대로 허용되지 않는다면 예외를 던지는 대신 '스마트 한'무언가를 시도하는 것이 좋습니다.

라이스


0

프로그래밍 오류를 나타내는 상황에 처해 있고 응용 프로그램 실행을 중지하려는 경우에만 예외를 발생시켜야합니다. 따라서 예외를 발생시키는 가장 좋은 방법은 NSAssert 및 NSParameterAssert 매크로를 사용하고 NS_BLOCK_ASSERTIONS가 정의되어 있지 않은지 확인하는 것입니다.


0

사례의 샘플 코드 : @throw ([NSException exceptionWithName : ...

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

}

}


-7

비즈니스 규칙 예외를 나타 내기 위해 객관적인 C에서 일반적으로 예외를 사용하지 않는 이유는 없습니다. 애플은 걱정하는 NSError를 사용할 수 있다고 말할 수있다. Obj C는 오래 전부터 사용되어 왔으며 한 번에 모든 C ++ 문서에서도 같은 말을했습니다. 예외를 던지고 잡는 데 드는 비용이 중요하지 않은 이유는 예외의 수명이 매우 짧고 정상적인 흐름에 대한 예외이기 때문입니다. 나는 내 인생에서 누군가가 말하는 것을들은 적이 없다.

또한 목표 C 자체가 너무 비싸고 대신 C 또는 C ++로 코딩한다고 생각하는 사람들이 있습니다. 따라서 항상 NSError를 사용한다는 것은 잘못된 정보와 편집증입니다.

그러나이 스레드의 문제는 아직 예외를 던지는 가장 좋은 방법은 무엇입니까? NSError를 반환하는 방법은 분명합니다.

[NSException raise : ... @throw [[NSException alloc] initWithName .... 또는 @throw [[MyCustomException ...?

여기에서 확인 / 확인되지 않은 규칙을 위와 약간 다르게 사용합니다.

(여기에서 Java 메타포 사용) 확인 / 선택 취소의 실제 차이점은 중요합니다-> 예외에서 복구 할 수 있는지 여부입니다. 그리고 복구로 나는 단지 충돌하지 않음을 의미합니다.

따라서 복구 가능한 예외에 @throw와 함께 사용자 정의 예외 클래스를 사용합니다. 여러 @catch 블록에서 특정 유형의 실패를 찾는 일부 앱 메소드가 있기 때문입니다. 예를 들어 내 앱이 ATM 시스템 인 경우 "WithdrawalRequestExceedsBalanceException"에 대한 @catch 블록이 있습니다.

NSException : raise를 사용하면 예외를 더 높은 수준에서 잡아서 기록하는 것 외에는 예외에서 복구 할 수 없으므로 런타임 예외에 사용됩니다. 그리고이를위한 커스텀 클래스를 만들 필요는 없습니다.

어쨌든 그게 내가하는 일이지만, 더 좋고 비슷한 표현 방법이 있다면 나도 알고 싶습니다. 내 자신의 코드에서는 오래 전에 C 코딩을 중단했기 때문에 API에 의해 전달 된 경우에도 NSError를 반환하지 않습니다.


4
"객관적 C에서 예외를 일반적으로 사용하지 않을 이유가 없습니다"와 같은 일반 문을 만들기 전에 일반적인 오류 사례 흐름의 일부로 예외를 사용하여 서버를 프로그래밍하는 것이 좋습니다. 믿거 나 말거나, ObjC에서 고성능 응용 프로그램 (또는 최소한 응용 프로그램의 일부)을 작성해야하는 이유가 있으며 예외를 던지면 일반적으로 성능이 심각하게 저하됩니다.
jbenet

6
코코아에서 예외를 사용하지 않는 이유는 매우 많습니다. 자세한 여기 빌 범 가너의 답변을 참조하십시오 stackoverflow.com/questions/3378696/iphone-try-end-try/... . 그는 자신이 말하는 것을 알고 있습니다 (힌트 : 고용주 확인). Cocoa의 예외는 복구 할 수없는 오류로 취급되며 시스템을 불안정한 상태로 둘 수 있습니다. NSError는 일반적인 오류를 전달하는 방법입니다.
Brad Larson

예외는 예외 입니다. 비즈니스 규칙 실패는 반드시 자격이 없습니다. "예외가 많은 코드를 찾아서 설계하면 성능이 향상 될 수 있습니다." codinghorror.com/blog/2004/10/…을
Jonathan Watmough

3
블록에서 예외를 처리 할 수 ​​없습니다. ARC 환경에서 예외가 발생하면 프로그램이 누출 될 수 있습니다. 따라서 공감.
Moszi

"예외를 던져 버리는 데 비용이 많이 드는 것은 중요하지 않습니다."성능이 중요한 에뮬레이터를 작성하고 있습니다. 비싼 예외를 던질 수는 없습니다.
NobodyNada
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.