10

Appleの並行性プログラミングガイドでは、NSOperationサブクラスの例(非並行型と並行型の両方)で例外処理が使用されており、なぜこれらが操作内でこのスタイルを推奨しているのか疑問に思っています。

リスト2-4キャンセルリクエストへの対応

- (void)main {
   @try {
      BOOL isDone = NO;

      while (![self isCancelled] && !isDone) {
          // Do some work and set isDone to YES when finished
      }
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}

私の理解では、一般的に例外処理はObjective-Cコードでは一般的な方法ではありません。例外は本質的にプログラマーのエラーであり、アプリをクラッシュさせるはずですが、予期しない入力はNSErrorで処理するのが最適です。(私の誤解されている可能性のある理解は、これこれのようなものから来ています)

NSOperationsが例外処理が重要である特定の状況を提示するのか、それともこれがそのガイドの特定の作成者のより好ましいスタイルであるのか疑問に思います。

ちなみに、NSOperationのサンプルコードの一部はこのスタイルに従いますが、他の例は従いません。ほとんどの視認性の高いOSSは、例外を使用しません(たとえば、AFNetworking)。

4

2 に答える 2

9

あなたの理解は正しいです-NSError(または同様のもの)は、例外ではなく、エラー情報を伝えるために使用されるべきです。ほとんどのObjective-Cコードは例外に対して安全ではなく、少なくともリソースをリークします。原則として、Appleであろうとサードパーティであろうと、コードが他の誰かのコードに例外を漏らさないようにしてください。一部のサードパーティフレームワークは、例外的に安全であることを明示的に示している場合がありますが、それはまれです。

その原則により、メソッドにキャッチオール例外ハンドラーが必要な理由がわかりますmain。ただし、実際には別の理由があります。操作は専用スレッドで実行されます。操作からスローされた例外はスタックに伝播しますが、それ以上は伝播しません。論理的な呼び出し元または操作の所有者は、別のスレッドで実行されている(またはまったく実行されていない)ため、それらを取得しません。したがって、リークされた例外は、プログラム全体を強制終了するか、他の兆候なしに黙って飲み込まれます。その後、プログラムが奇妙な状態でスタックする可能性があります。エラーが発生したことに気づかなかったため、操作の結果が届かないのを待ち続けることができます。

さらに、Appleには、同時実行プログラミングガイドに、エラーと例外の処理について説明しているセクションがあります。「個別の実体」に関する彼らの最初のポイントは、前の段落で私が言ったことをほのめかしています。

エラーと例外の処理

操作は基本的にアプリケーション内の個別のエンティティであるため、発生するエラーや例外を処理する責任があります。OS X v10.6以降では、NSOperationクラスによって提供されるデフォルトのstartメソッドは例外をキャッチしません。(OS X v10.5では、startメソッドは例外をキャッチして抑制します。)独自のコードは常に例外を直接キャッチして抑制します。また、エラーコードを確認し、必要に応じてアプリケーションの適切な部分に通知する必要があります。また、startメソッドを置き換える場合は、カスタム実装で例外をキャッチして、基になるスレッドのスコープを離れないようにする必要があります。

処理する準備が必要なエラー状況のタイプには、次のものがあります。

  • UNIXのerrnoスタイルのエラーコードを確認して処理します。
  • メソッドと関数によって返される明示的なエラーコードを確認します。
  • 独自のコードまたは他のシステムフレームワークによってスローされた例外をキャッチします。
  • NSOperationクラス自体によってスローされた例外をキャッチします。これにより、次の状況で例外がスローされます。
    • 操作を実行する準備ができていないが、そのstartメソッドが呼び出された場合
    • 操作が実行中または終了したとき(おそらくキャンセルされたため)、そのstartメソッドが再度呼び出されたとき
    • すでに実行中または終了している操作に完了ブロックを追加しようとした場合
    • キャンセルされたNSInvocationOperationオブジェクトの結果を取得しようとしたとき

カスタムコードで例外またはエラーが発生した場合は、そのエラーをアプリケーションの残りの部分に伝播するために必要な手順を実行する必要があります。NSOperationクラスは、エラー結果コードまたは例外をアプリケーションの他の部分に渡すための明示的なメソッドを提供しません。したがって、そのような情報がアプリケーションにとって重要である場合は、必要なコードを提供する必要があります。

于 2012-12-02T17:45:46.230 に答える
3

この投稿とそれに付随する回答は、一般的な例外処理と例外処理なしのトピックについて非常によく説明していると思います。

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

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

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

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

だから、全体として、投げないでください。

おっしゃるように、CocoaAPIにはこれに対するいくつかの回避策があります。nilを返すこととNSError**パターンはそれらの2つです。

于 2012-12-02T16:41:01.960 に答える