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