Objective-C/Cocoaで例外をスローする最良の方法は何ですか?
次のように[NSException raise:format:]
を使用します。
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
ここで注意点。 Objective-Cでは、多くの同様の言語とは異なり、通常は通常の操作で発生する可能性のある一般的なエラー状況に対して例外を使用しないようにする必要があります。
Obj-C 2.0に関するAppleのドキュメント は次のように述べています:「重要:Objective-Cでは例外はリソースを大量に消費します。一般的なフロー制御に例外を使用したり、単にエラー(ファイルにアクセスできません)」
Appleの概念的な例外処理ドキュメント は同じことを説明していますが、より多くの言葉で説明しています。「重要:プログラミングまたは例外のコレクションアクセス、変異の試みなどの予期しないランタイムエラーのために例外の使用を予約する必要があります不変オブジェクト、無効なメッセージの送信、ウィンドウサーバーへの接続の喪失。通常、実行時ではなくアプリケーションの作成時に、例外を伴うこれらの種類のエラーを処理します。[.....]例外の代わりに、エラーオブジェクト(NSError)とCocoaエラー配信メカニズムは、Cocoaアプリケーションで予想されるエラーを伝えるための推奨される方法です。
この理由の一部は、Objective-Cのプログラミングイディオム(単純なケースでは戻り値を使用し、より複雑なケースでは参照パラメーター(多くの場合NSErrorクラス)を使用する)、一部は例外のスローとキャッチがはるかに高価であり、最後に(そして最も重要なのは)、Objective-C例外がCのsetjmp()およびlongjmp()関数の薄いラッパーであり、基本的に注意深いメモリ処理を台無しにしていることです この説明 を参照してください。
@throw([NSException exceptionWith…])
EJamesの応答についてコメントする担当者がいないので、ここに私のものを置く必要があると思います。 Javaのバックグラウンドから来た人は、JavaがExceptionとRuntimeExceptionを区別することを思い出してください。例外はチェック例外であり、RuntimeExceptionはチェックされていません。特に、Javaでは、「通常のエラー状態」にはチェック例外を使用し、「プログラマーエラーに起因する実行時エラー」には未チェック例外を使用することをお勧めします。 Objective-Cの例外は、チェックされていない例外を使用するのと同じ場所で使用する必要があり、チェックされた例外を使用する場所では、エラーコードの戻り値またはNSError値が推奨されます。
ObjC 2.0、Objective-C例外はCのsetjmp()longjmp()のラッパーではなく、C++例外と互換性があるため、@ tryは「無料」ですが、例外のスローとキャッチははるかに高価です。
とにかく、アサーション(NSAssertおよびNSCAssertマクロファミリを使用)はNSExceptionをスローし、それらをRies状態として使用するのは正気です。
NSErrorを使用して、例外ではなく失敗を伝えます。
NSErrorに関する簡単なポイント:
NSErrorを使用すると、Cスタイルのエラーコード(整数)で根本原因を明確に特定でき、エラーハンドラーでエラーを解決できるようになります。 NSErrorインスタンスでSQLiteのようなCライブラリのエラーコードを非常に簡単にラップできます。
NSErrorにはオブジェクトであるという利点もあり、userInfoディクショナリメンバーを使用してエラーをより詳細に記述する方法を提供します。
しかし何よりも、NSErrorをスローすることはできないため、エラー処理へのより積極的なアプローチを奨励します。これは、ホットポテトを単に呼び出してコールスタックをさらに上げてユーザーにのみ報告できる他の言語とは対照的です。意味のある方法で処理されていない(OOPの情報隠蔽の最大の教義に従うことを信じていない場合).
参照リンク: 参照
これが、「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];
Try catchブロックで例外を発生させるために2つの方法を使用できます
@throw[NSException exceptionWithName];
または2番目の方法
NSException e;
[e raise];
通常のプログラムフローを制御するために、例外を使用しないでください。ただし、一部の値が目的の値と一致しない場合は常に例外をスローする必要があります。
たとえば、一部の関数が値を受け入れ、その値が決してnilになることを許可されていない場合、何か「スマート」にしようとするのではなく、例外をスローすることは問題ありません...
リース
ケースのサンプルコード:@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
}
}
プログラミングエラーを示す状況にあり、アプリケーションの実行を停止する場合にのみ、例外をスローする必要があります。したがって、例外をスローする最良の方法は、NSAssertマクロとNSParameterAssertマクロを使用し、NS_BLOCK_ASSERTIONSが定義されていないことを確認することです。