Objective-c/cocoaで例外をスローする最良の方法は何ですか?
13 に答える
私は次のように使用[NSException raise:format:]
します:
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
ここで一言注意。Objective-C では、多くの同様の言語とは異なり、通常、通常の操作で発生する可能性のある一般的なエラー状況に対して例外を使用しないようにする必要があります。
Obj-C 2.0 に関する Apple のドキュメントには次のように記載されています。
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 の最大の情報隠蔽の原則に従うと信じている場合はそうではありません)。
参考リンク: 参考
try catch ブロックで例外を発生させるには、2 つの方法を使用できます。
@throw[NSException exceptionWithName];
または2番目の方法
NSException e;
[e raise];
通常のプログラムフローを制御するために例外を使用してはいけないと思います。ただし、一部の値が目的の値と一致しない場合は常に例外をスローする必要があります。
たとえば、ある関数が値を受け入れ、その値がnilになることは決して許されない場合、「スマート」なことをしようとするのではなく、例外をスローするのは問題ありません。
リース
プログラミング エラーを示す状況に陥り、アプリケーションの実行を停止したい場合にのみ、例外をスローする必要があります。したがって、例外をスローする最善の方法は、NSAssert および NSParameterAssert マクロを使用し、NS_BLOCK_ASSERTIONS が定義されていないことを確認することです。
ビジネス ルールの例外を意味する場合でも、オブジェクティブ C で通常は例外を使用しない理由はありません。Apple は、気にする NSError を使用すると言うことができます。Obj C は長い間存在しており、かつてはすべての C++ ドキュメントが同じことを述べていました。例外のスローとキャッチのコストがどれほど高くても問題にならない理由は、例外の有効期間が非常に短く、... 通常のフローの例外であるためです。私の人生で誰かが言うのを聞いたことがありません。例外がスローされてキャッチされるまでに長い時間がかかりました。
また、目的の C 自体が高すぎると考えて、代わりに C や C++ でコーディングする人もいます。したがって、常に NSError を使用すると言うのは、情報不足で妄想的です。
しかし、このスレッドの質問にはまだ答えがありません。例外をスローする最良の方法は何ですか。NSError を返す方法は明らかです。
[NSException raise:... @throw [[NSException alloc] initWithName.... または @throw [[MyCustomException... ?
ここでは、チェック済み/未チェックのルールを上記とは少し異なる方法で使用しています。
(ここでは Java メタファーを使用して) チェックされているかチェックされていないかの実際の違いは重要です --> 例外から回復できるかどうか。そして、回復とは、クラッシュしないだけではないことを意味します。
そのため、複数の @catch ブロックで特定の種類のエラーを探すアプリ メソッドを使用する可能性が高いため、回復可能な例外に対して @throw でカスタム例外クラスを使用します。たとえば、私のアプリが ATM マシンの場合、「WithdrawalRequestExceedsBalanceException」の @catch ブロックを作成します。
より高いレベルで例外をキャッチしてログに記録する以外に、例外から回復する方法がないため、ランタイム例外には NSException:raise を使用します。そのためのカスタム クラスを作成しても意味がありません。
とにかくそれが私がしていることですが、より良い、同様に表現力のある方法があれば、私も知りたいです. 私自身のコードでは、ずっと前に C のコーディングをやめて以来、API から渡されたとしても NSError を返すことはありません。