답변:
글쎄, 일반적으로 런타임에 오류가 발생할 수있는 메소드가 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 에 대한 훌륭하고 간단한 자습서도 있습니다 .
id
A를 BOOL
. 약간의 ARC 호환 변형이 많이 인정 될 것입니다.
BOOL
. NO
오류가 발생한 경우 반환 하고 반환 값을 확인하는 대신 확인하십시오 error
. 경우하는 nil
경우, 가서 != nil
손잡이를.
**error
이 아닌 코드를 통합해야합니다 . 그렇지 않으면 프로그램이 완전히 비 친절한 오류를 발생시키고 무슨 일이 일어나고 있는지 분명하게 나타내지 않습니다.
가장 최근의 구현을 기반으로 몇 가지 제안을 추가하고 싶습니다. 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];
Constants+Macros.h
파일에 넣고이 파일을 접두사 헤더 ( .pch
파일)로 가져와 어디서나 사용할 수 있습니다. 2 개의 매크로 중 하나만 사용하고 있다면 효과가있을 수 있습니다. 아마도 이것을 테스트하지는 않았지만로 변환 int
하는 NSString
것이 실제로 필요하지는 않습니다.
.strings
파일) 에 있어야합니다 . Apple의 매크로가 표시되는 위치이기 때문입니다. 사용에 대해 읽으 NSLocalizedStringFromTable
십시오 : developer.apple.com/library/mac/documentation/cocoa/conceptual/…
FS_ERROR_LOCALIZED_DESCRIPTION
라는 파일에서 지역화 가능한 문자열을 확인합니다 FSError.strings
. .strings
외국어 파일 인 경우 Apple의 현지화 안내서를 확인하십시오 .
좋은 대답 알렉스. 잠재적 인 문제 중 하나는 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;
...
난 당신이 반환해야하기 때문에 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");
}
}];
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.