57

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

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

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

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

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

4

7 に答える 7

69

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

例外をスローするreleaseと、スタックをアンワインドしてスキップした呼び出しによってリークが発生します。これは、プロセスが終了するとすべてのリソースが OS に返されるため、回復しないことが確実な場合にのみ、スローするように制限する必要があります。

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

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

だから、とにかく投げないでください。

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

ARC の説明

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

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

Objective-C の完全な例外安全性はデフォルトで無効になっています。これは、Clang の作成者が Objective-C プログラムが例外から回復しないことを前提としており、そのクリーンアップに関連して大きなコード サイズのコストと小さなパフォーマンス ペナルティがあるためです。一方、Objective-C++ では、C++ はすでに多くのクリーンアップ コードを導入しており、人々は実際に例外安全性を必要とする可能性がはるかに高くなります。

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

于 2011-01-10T16:54:04.987 に答える
36

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 アプリケーションで使用できます。ただし、予想される実行時例外がこれらのサブシステムから抜け出して、呼び出し元のコードで終了しないようにする必要があります。たとえば、構文解析ライブラリは内部的に例外を使用して問題を示し、深く再帰的になる可能性のある構文解析状態から迅速に終了できるようにする場合があります。ただし、ライブラリのトップ レベルでそのような例外をキャッチし、適切なリターン コードまたは状態に変換するように注意する必要があります。

于 2011-01-10T16:52:45.600 に答える
8

NSErrorプログラムの実行中に発生する例外的な状態には型エラー処理を使用する必要があるのに対し、例外はプログラマーのエラーをキャッチするために使用する必要があると思います。

を返すnilことに関しては、それだけではありません。問題がある可能性のある関数はnil、 を返すだけでなく、パラメーターとして渡される NSError オブジェクトを使用して詳細情報を提供することもできます (また提供する必要があります)。

こちらもご覧ください

于 2011-01-10T16:33:24.590 に答える
3

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

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

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

于 2011-01-10T17:16:33.633 に答える
2

使用されるエラー処理のタイプは、実行しようとしていることによって異なります。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
}

お役に立てれば。

于 2011-01-10T16:42:07.167 に答える
1

あなたのトレーナーは正しいと思います。例外に対してはあらゆる種類の議論を行うことができますが、経験豊富な Cocoa 開発者にあなたのコードを正しく「匂い」させたい場合は、Apple がコードで使用しているのと同じ設計パターンを使用してアプリケーションを実装し、彼らのフレームワークで。つまり、例外ではなくnils とs です。NSError

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

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

(また、Appleがいくつかの場所で例外を使用していることも指摘しておく必要があります。通常は、API を間違って使用している場合です。)

于 2011-01-10T16:44:17.923 に答える
0

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

クライアントの例外処理の期待に関する仕様はありません。ドキュメントに基づいてプログラムを維持する必要があります (退屈でエラーが発生しやすく、おそらく例外がスローされるまで維持されません)。

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

通常予想されるように、プログラムの流れを制御するためにそれらを使用することは避けるべきです (よくある誤用)。

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

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

于 2011-01-10T17:02:34.800 に答える