web-dev-qa-db-ja.com

Objective-Cの例外

IPhoneアプリプログラミングコースを修了しました。コースの一環として、私は見た

  • Objective-Cは、@tryディレクティブを使用して例外処理を提供します
  • システムライブラリは例外処理を使用せず、return nilを優先します

書いた新しいコードに例外処理を使用するかどうかを尋ねました(たとえば、フロントエンドとロジックの両方のコードを書く場合、それらの間の通信は私の手にあります)。コード。 (しかし、彼は詳しく説明するのに失敗し、その後クラスが進んだ。おそらくその理由は後で明らかになると思う。)

確かに例外はreturn nilより優れていますか?特定の型をキャッチできます。通常は成功する関数の戻り値の型を無視することでそれらを無視することはできません。ログに記録できるテキストメッセージがあるため、コードは通常のケースに集中できるため、読みやすくなります。 例外を使用する理由

それで、あなたはどう思いますか?私のトレーナーはObjective-Cの例外を使用しない権利がありましたか?もしそうなら、なぜですか?

52
Adrian Smith

リソースが自動的に管理されない状況で例外をスローすることは安全ではありません。これは、Cocoaフレームワーク(および隣接フレームワーク)の場合で、手動の参照カウントを使用するためです。

例外をスローすると、スタックを巻き戻してスキップしたrelease呼び出しはリークになります。これは、プロセスの終了時にすべてのリソースがOSに返されるため、回復しないことが確実な場合にのみ、スローを制限する必要があります。

残念ながら、NSRunLoopsはそれらに伝播するすべての例外をキャッチする傾向があるため、イベント中にスローすると、次のイベントに戻ります。これは明らかに、非常に悪いことです。したがって、単に投げない方が良いです。

ガベージコレクションされたObjective-Cを使用すると、Objective-Cオブジェクトで表されるリソースが適切に解放されるため、この問題は軽減されます。ただし、Objective-CオブジェクトにラップされていないCリソース(ファイル記述子やmalloc- allocatedメモリなど)は引き続きリークします。

だから、全部で、投げないでください。

あなたが述べたように、Cocoa APIにはこれに対するいくつかの回避策があります。 nilNSError**パターンを返すのはそのうちの2つです。

ARCの説明

ARCユーザーは、完全な例外安全を有効または無効にすることができます。例外の安全性が有効になっている場合、ARCはスコープが削除されたときに強力な参照を解放するコードを生成し、コードで例外を安全に使用できるようにします。 ARCは外部ライブラリにパッチを適用して例外サポートを有効にしません。したがって、プログラムで例外サポートが有効になっている場合でも、スローする場所(特にキャッチする場所)に注意する必要があります。

ARC例外サポートは、-fobjc-arc-exceptionsで有効にするか、-fno-objc-arc-exceptionsで無効にすることができます。デフォルトでは、Objective-Cでは無効になっていますが、Objective-C++では有効になっています。

Clangの作成者は、Objective-Cプログラムがとにかく例外から回復しないと想定しているため、Objective-Cの完全な例外安全性はデフォルトで無効になっています。一方、Objective-C++では、C++にはすでに多くのクリーンアップコードが導入されており、人々は実際に例外安全性を必要とする可能性が非常に高くなります。

これはすべて LLVM WebサイトのARC仕様 からのものです。

65
zneak

CocoaおよびiOSプログラミングでは、例外は、回復不能なプログラマーエラーを示すために使用されます。フレームワークによって例外がスローされた場合、フレームワークが、回復可能ではなく、現在内部状態が未定義であるエラー状態を検出したことを示します。

同様に、フレームワークコードを介してスローされた例外は、フレームワークを未定義状態のままにします。つまり次のようなことはできません:

void a() {
    @throw [MyException exceptionWithName: @"OhNoes!"  ....];
}

@try {
    ... call into framework code that calls a() above ...
} @catch (MyException e) {
    ... handle e ...
}

結論:

Cocoaでは、フロー制御、ユーザー入力の検証、データ有効性の検出、または回復可能なエラーを示すために例外を使用しないでください。そのためには、NSError**パターンを ここに記載 (Abizemに感謝)として使用します。

(これに違反する少数のAPIがあることに注意してください。例外は、回復可能な状態を示すために使用されます。これらのバグを報告し、これらの不整合を廃止し、最終的に削除します。)


最後に ドキュメントを見つけました 私は探していました:

重要:プログラミングの例外、または範囲外のコレクションアクセス、不変オブジェクトの変更の試行、送信などの予期しないランタイムエラーの例外の使用を予約する必要があります無効なメッセージ、およびウィンドウサーバーへの接続の喪失。通常、これらの種類のエラーは、実行時ではなくアプリケーションの作成時に例外を付けて処理します。

エラー条件を処理するために例外を使用する既存のコード本体(サードパーティライブラリなど)がある場合、Cocoaアプリケーションでそのままのコードを使用できます。ただし、予想される実行時例外がこれらのサブシステムからエスケープされて、呼び出し元のコードで終了しないようにする必要があります。たとえば、解析ライブラリは内部で例外を使用して問題を示し、深く再帰的な解析状態からの迅速な終了を可能にします。ただし、ライブラリの最上位でこのような例外をキャッチし、適切な戻りコードまたは状態に変換するように注意する必要があります。

34
bbum

私が間違っている場合、プログラマーのエラーをキャッチするために例外を使用し、プログラムの実行中に発生する例外条件にはNSErrorタイプのエラー処理を使用する必要があると思います。

そして、nilを返すことに関しては、それだけではありません。問題があるかもしれない関数は、単にnilを返すだけではなく、NSErrorオブジェクトを使用して、さらに情報を提供できますパラメータとして渡されます。

こちらもご覧ください

7
Abizern

個人的には、例外を使用しない理由はありません独自のコードで。例外パターンは、エラーを処理するよりもクリーンです

  • 常に戻り値を持ち、
  • 可能な戻り値の1つが本当に「エラー」を意味することを確認する
  • NSError*への参照である追加のパラメーターを渡す
  • エラーが返されるたびにコードをクリーンアップコードで散らしたり、一般的なエラークリーンアップセクションにジャンプするための明示的なgotoを使用したりします。

ただし、他の人のコードは例外を適切に処理することが保証されていないことに注意する必要があります(純粋なCの場合、実際にはca n'tハンドル例外)。したがって、例外がコードの境界を越えて伝播することを決して許可しないでください。たとえば、デリゲートメソッドの奥深くで例外をスローした場合、デリゲートメッセージの送信者に戻る前にその例外を処理する必要があります

3
JeremyP

使用されるエラー処理のタイプは、達成しようとしているwhatに依存します。 Appleのほとんどすべてのメソッドは、エラー時にアクセスできるNSErrorオブジェクトを生成します。

このタイプのエラー処理は、コードのセクション内のtry-catchブロックと組み合わせて使用​​できます。

-(NSDictionary *)getDictionaryWithID:(NSInteger)dictionaryID error:(NSError **)error
{
    @try
    {
         //attempt to retrieve the dictionary
    }
    @catch (NSException *e)
    {
        //something went wrong
        //populate the NSError object so it can be accessed
    }
}

簡単に言うと、メソッドの目的によって、使用するエラー処理のタイプが決まります。ただし、上記のこの例では、両方を使用できます。

Try-catchブロックの一般的な使用法:

@try
{
    [dictionary setObject:aNullObject forKey:@"Key"];
}
@catch (NSException *e)
{
    //one of the objects/keys was NULL
    //this could indicate that there was a problem with the data source
}

お役に立てれば。

2
Evan Mulawski

あなたのトレーナーは正しいと思います。例外に対してすべての種類の引数を作成できますが、一番下の行は、経験豊富なCocoa開発者にコードを「匂わせる」場合は、Appleは、コードとフレームワークで使用します、つまり、例外ではなくnilsとNSErrorsを使用します。

したがって、例外を使用しないことの利点は、主に一貫性と親しみやすさにあります。

考慮すべきもう1つのことは、Objective Cのnilは通常かなり「安全」であることです。つまり、任意のメッセージをnilに送信でき、アプリケーションはクラッシュしません。

(また、Apple doは、通常はAPIを誤って使用している場所で例外を使用します。)

1

例外を好む場合は、それらを使用できます。そうした場合、保守が非常に難しくなるため、それらを控えめに使用することをお勧めします(プログラムの正確性を証明するために通常実行時に実行する必要がある追加の出口点)。

クライアントに期待される例外処理の仕様はありません。ドキュメントに基づいてプログラムをメンテナンスする必要があります(退屈でエラーが発生しやすいため、例外がスローされるまでメンテナンスされない可能性があります)。

私がそれらを使用する場合、非常にまれな場合にのみ使用し、エラーコードを使用するか、可能な場合はnilを返します。さらに、ライブラリの内部で使用し、クライアントをインターフェイスの例外(または副作用)にさらさないようにします。 objcやc ++では使用しません(まあ、キャッチしますが、投げません)。

通常予想されるように、それらを使用してプログラムフローを制御することは避けてください(よくある誤用)。

特定のクラッシュを回避する必要がある場合は、それらを使用する方が適切です。すぐにコードを作成する場合は、プログラムフローの一部としてエラーを処理するインターフェイスを作成し、可能な限り例外を作成しないことをお勧めします。

元の投稿に対する修正:ココアライブラリはnilを返すことを好みます。場合によっては、(キャッチするために)例外をスローします。

0
justin