본문 바로가기
프로그래밍 놀이터/iOS

[Objective-C] 예외와 에러

by 돼지왕 왕돼지 2018. 1. 9.
반응형

 [Objective-C] 예외와 에러


출처 : OS X 구조를 이해하면서 배우는 Objective-C Chap 18.

#define, &, & 연산자, ..., @catch, @catch 의 특수 구문, @finally, @throw, @try, arc, arc exception, assertion, C 언어 전역 탈출용 함수, D NS _BLOCK_ASSERTIONS, error, error-responder chain, errorWithDomain, exception, exception handler, exception handling domain, localizedDescription, localizedFailureReason, localizedRecoverySuggestion, longjmp, NSAssert, NSAssertionHandler, NSCAssert, NSCParameterAssert, nserror, NSError** error, NSException, NSFilePathErrorKey, NSInternalInconcsistencyExcepion, NSInvalidArgumentException, NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, NSLocalizedRecoveryOptionsErrorKey, NSLocalizedRecoverySuggestionErrorKey, NSParameterAssert, NSRangeException, NSRecoveryAttempterErrorKey, NSStringEncodingErrorKey, NSUnderlyingErrorKey, NSURLErrorKey, NS_BLOCK_ASSERTIONS, Objective-C 예외 처리, Raise, setjmp, uncaught exception handler, var arg 매크로, [Objective-C] 예외와 에러, __VA_ARGS__, 가정, 검증, 매크로, 변수 포인터, 어설션, 에러, 에러 처리, 예외, 예외 발생과 전파, 예외 전파, 예외 처리, 예외 처리 개념, 예외 처리 구조 개요, 예외 처리 프로그래밍 시 주의사항, 예외 핸들러와 예외 처리 도메인, 중첩한 메서드 호출 안에서 예외 발생, 직접 예외 발생시키기, 카운터 관리 방식과 예외, 컴파일 옵션, 포인터의 포인터, 표명


18.1. 예외란


* 18.1.1. 예외 처리 개념


-

예외(exception)는 프로그램의 정상적인 실행을 중단하고 대응해야 할 특별한 상황을 뜻한다.


예를 들어 NSMutableArray 의 메서드 addObject: 는 인수 객체를 리시버인 배열에 추가하는데, 인수가 nil 일 때는 부정한 인수라는 걸 나타내는 NSInvalidArgumentException 가 발생한다.

메서드 objectAtIndex: 로 참조하려고 한 인덱스가 요소 개수를 벗어나면 범위 오류를 나타내는 예외 NSRangeException 이 발생한다.




* 18.1.2. Objective-C 예외 처리


-

예외가 일어난 것을 이용하는 프로그램이나 예외를 일으켜서 동작을 제어하는 프로그래밍 스타일을 추천하지 않는 게 애플의 방침.



-

Objective-C 에서 예외 처리 구조의 이용 목적은 우선 소프트웨어 개발, 특히 테스트 등에서 발생하는 예외를 파악해서 디버그 작업에 도움을 주는 것이 목적이다.

완성한 소프트웨어에는 예외 처리를 포함하지 않는다.



-

앱에서 예외가 발생했을 때 그 영향이 전체에 퍼지지 않도록 필요한 처리를 한 후 신속하게 종료할 목적으로 예외 처리를 사용한다.



-

어느 쪽이더라도 Objective-C 에서 예외는 ‘잘 안 되면 다시 한번 해보자’는 생각으로 사용하는 것이 아니라 ‘큰 일 나기 전에 가능한 범위에서 대응하고 프로그램을 중단시키자’라는 비상 탈출 경로로 인식해야 한다.




18.2. 예외 처리 구조 개요


* 18.2.1. 예외 핸들러와 예외 처리 도메인


-

예외가 발생했을 때 처리하기 위해 준비한 루틴을 예외 핸들러(exception handler)f라고 부른다.

그리고 그 예외 핸들러가 처리할 대상인 코드 부분을 예외 처리 도메인(exception handling domain)이라고 부른다.




* 18.2.2. 예외를 나타내는 클래스 NSException


-

Cocoa 환경에 예외를 나타내는 클래스로 NSException 이 있다.

NSException 의 인스턴스를 생성하고 여기에 raise 메시지를 보내서 예외 처리를 할 수 있다.




* 18.2.3. 예외 처리 구조 구문


-

@try{


}

@catch (NSException *exception){


}

@finally{


}



-

@catch 블록은 여러 개 올 수 있다.

@catch 블록을 작성하지 않을 수도 있는데, 그때는 @finally 블록이 반드시 필요하다.




* 18.2.4. 간단한 예외 처리 예제 프로그램


-

런타임 시스템의 미처리 예외 핸들러(uncaught exception handler)라는 루틴이 예외를 잡아서 처리할 수 있다.

예외 처리 도메인 외부에서 예외가 발생한 경우나 프로그램의 예외 핸들러로 예외 처리가 끝나지 않을 경우 미처리 예외 핸들러는 메시지를 단말이나 콘솔 또는 로그 파일에 출력하고 프로그램을 종료시킵니다.




18.3. 예외 발생과 전파


* 18.3.1. 예외 전파



* 18.3.2. 직접 예외 발생시키기


-

예외를 발생시키려면 앞에서 본 것처럼 예외 객체에 raise 메시지를 보낸다.

하지만 실제로는 클래스 NSException 의 클래스 메서드를 사용하는 것이 편리하다.


+(void) raise:(NSString*)nameformat:(NSString*)format, ...




* 18.3.3. 예외를 발생시키는 @throw 문


-

NSException 인스턴스에 @throw 를 적용하는 건 메시지 raise 를 보내는 것과 같다.



-

@throw 는 @catch 블록 안에서 인수 없이 사용할 수 있는데,

이는 @catch 블록에서 잡은 예외 객체를 인수로 지정한 것과 같은 효과이다.



-

@catch 블록은 여러 개 배열할 수 있지만 그 순서가 중요하다.

@throw 로 예외를 발생시킬 때 사용한 객체가 어떤 @catch 블록의 인수와 일치하는지 위에서부터 차례로 확인하기 때문이다.




* 18.3.4. @catch 의 특수 구문


-

@catch (…){ // ... 은 생략한 것이 아닌 실제 ... 이다


}


이는 예외는 잡지만 예외 내용은 몰라도 될 때 이용한다.

그 외에도 @catch 블록이 있을 경우에는 마지막 블록으로 작성해야 한다.




* 18.3.5. 예외 전파와 @finally


-

@catch 블록 안에서 예외가 발생(또는 전파)했을 경우 또는 발생한 예외를 처리할 수 있는 @catch 블록이 없을 경우 보통은 곧바로 외부의 예외 핸들러로 처리가 옮겨간다.

하지만 @finally 블록이 있으면 외부 예외 핸들러로 처리를 옮기기 전에 @finally 블록 내용을 실행한다.

@finally 블록은 예외가 발생하지 않았을 경우에는 @try 블록 다음에 실행되고 예외가 외부로 전파되지 않을 경우에는 @catch 믈록 다음에 반드시 실행된다.




* 18.3.6. 예외 처리 프로그래밍 시 주의사항


** 중첩한 메서드 호출 안에서 예외 발생


** 카운터 관리 방식과 예외


-

메모리 관리로 카운터 관리 방식을 사용할 때 예외가 발생해 예외 핸들러가 처리를 시작하면 지금까지 사용하고 있던 객체가 제대로 해제되지 않을 수도 있다.

이건 수동 카운터 관리 방식에서도 ARC 에서도 같다.

ARC 는 약한 참조 변수를 제로화하지 못하는 문제도 발생한다.


이런 점이 Objective-C 에서 예외 처리 구조를 전제로 프로그램을 작성하지 말라는 의미이기도 하다.



** C 언어 전역 탈출용 함수


-

C 언어는 setjmp() 와 longjmp() 함수가 있어서 함수 호출의 단계에서 단번에 빠져나올 수 있다.

하지만 여기서 설명하는 예외 처리 구조가 이런 기능으로 구현되므로 예외 처리 구조와 이런 함수를 동시에 사용하면 안 된다.

Objective-C 언어에 C 언어 루틴을 조합해서 사용할 때는 setjmp() 와 longjmp() 가 C 언어 루틴 안에서 처리가 끝나도록 해야 한다.






18.4. 어설션 ( Assertion )


* 18.4.1. 어설션이란


-

디버그 목적으로 프로그램이 지켜야 하는 제약 조건을 만족시키는지 확인하고 싶을 때가 있다.

프로그램이 지켜야만 하는 조건을 프로그램 안에 기술해서 그 조건을 지키지 않았을 때 예외를 발생시키는 구조가 어설션(assertion)이다.

표명, 검증, 가정이라고도 한다.



-

조건이 거짓이 되면 예외 NSInternalInconcsistencyExcepion 이 발생한다.

실제로는 스레드마다 NSAssertionHandler 라는 클래스의 인스턴스가 자동으로 생성되어 그 객체가 예외를 발생시키는데, 이 클래스를 직접 조작해야 하는 일은 거의 없다.



-

어설션은 컴파일할 때 NS_BLOCK_ASSERTIONS 라는 매크로가 정의되어 있으면 코드에 들어가지 않는다.

아무것도 적지 않은 빈 문장과 같아진다.

따라서 완성판 프로그램을 컴파일할 때는 컴파일러 옵션에 -D NS _BLOCK_ASSERTIONS 를 지정한다.

옵션 -D 는 명령어 줄에서 매크로를 정의할 때 쓴다.

이렇게 하면 파일 하나하나에 #define 을 적지 않아도 된다.



* 18.4.2. 어설션 매크로


-

NSParameterAssert(condition)

NSCParameterAssert(condition)


NSAssert(condition, NSString *description [, arg…])

NSCAssert(condition, NSString *description [, arg…])



-

개수가 변하는 인수를 가진 매크로


C언어의 새로운 규격에 가변 인수를 매크로에 쓸 수 있다.

매크로 매개변수 부분에 “…” 이 있고, 매크로 전개 부분에서 __VA_ARGS__ 로 참조한다.




18.5. 에러 처리


* 18.5.1. 에러 처리 구조의 목적



* 18.5.2. 에러를 표시하는 클래스 NSError 사용 방법


-

에러 객체는 함수나 메서드 반환값으로 호출한 쪽에 돌려줄 수도 있지만 대부분의 Cocoa API 는 에러가 발생할 가능성이 있는 메시지의 마지막 인수로 받는다.



-

(NSError** error) 는 NSError 인스턴스를 저장하는 변수 포인터로, 포인터의 포인터이다.

인스턴스를 저장하는 변수를 준비해서 & 연산자를 사용해 그 주소를 넘긴다.

지정한 파일이 없는 등의 에러가 발생하면 변수 err 에 에러 객체가 대입된다.

에러가 발생하지 않을 때는 객체가 대입되지 않으므로 에러가 발생하지 않았다는 걸 확실히 하기 위해 변수 err 에 미리 nil 을 대입해두는 습관을 기르는 것이 좋다.


ARC 로 메모리 관리할 때 NSError** 형 인수에는 메서드 안에 자동 변수의 어드레스 또는 nil 을 지정해야 한다.



-

앱이 지역화되면 경고 패널에 표시되는 메시지도 해당 언어로 표시된다.




* 18.5.3. 에러 객체에서 정보 얻기


-

-(NSString*) localizedDescription

-(NSString*) localizedFailureReason

-(NSString*) localizedRecoverySuggestion




* 18.5.4. 직접 에러 객체 만들기


-

+(id) errorWithDomain:(NSString*)domain code:(NSInteger)code userInfo:(NSDictionary*)dict



-

사용자 사전에 자유롭게 항목을 등록할 수 있지만 특정 목적을 위해 몇 가지 키가 미리 상수로 정의되어 있다.

값이 메시지를 나타내는 문자열이라면 기본적으로 지역화되어 있다.


NSLocalizedDescriptionKey                              에러 내용을 설명하는 문자열

NSLocalizedFailureReasonErrorKey               에러 원인을 설명하는 짧은 문자열

NSLocalizedRecoverySuggestionErrorKey     에러 대응 방법을 나타내는 문자열

NSLocalizedRecoveryOptionsErrorKey          복구 방법을 나타내는 버튼 제목의 문자열 배열

NSRecoveryAttempterErrorKey                         복구 처리하는 객체

NSUnderlyingErrorKey                                   에러 객체의 원인인 에러 객체

NSFilePathErrorKey                                        에러 관련 파일 경로의 문자열

NSStringEncodingErrorKey                              인코딩 번호를 포함한 NSNumber 객체

NSURLErrorKey                                             에러 관련 NSURL 객체




18.6. 에러 리스폰더 체인


-

에러 리스폰더 체인(error-responder chain) 은 Mac OS X 에서만 사용할 수 있다.





반응형

댓글