Swift 2:呼び出しはスローできますが、「try」とマークされておらず、エラーは処理されません


161

Xcode 7ベータ版をインストールし、SwiftコードをSwift 2に変換した後、理解できないコードの問題が発生しました。私はSwift 2が新しいのを知っているので、それについて何もないので検索して理解します。質問を書く必要があります。

ここにエラーがあります:

呼び出しはスローできますが、 'try'とマークされておらず、エラーは処理されません

コード:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

スナップショット: ここに画像の説明を入力してください

回答:


168

すでにsave()呼び出しに対して行っているのと同じようにエラーをキャッチする必要があります。ここでは複数のエラーを処理してtryいるため、次のように単一のdo-catchブロックで複数の呼び出しを順番に行うことができます。

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

または、@ bames53が以下のコメントで指摘しているように、スローされた場所でエラーをキャッチしない方がよい場合がよくあります。あなたは、のような方法をマークすることができthrows、その後tryメソッドを呼び出すこと。例えば:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

これは私がそれを理解するのに役立ちます、ありがとう。
ファルハッド

5
実際には、例外をここでキャッチする必要はありません。tryキーワードを関数呼び出しに追加するだけで、この関数をとして宣言できますfunc deleteAccountDetail() throw。または、指定された入力に対して関数がスローしないことが保証されている場合は、を使用できますtry!
bames53 2015年

4
私はこれをnitpickに持ち込みませんが、例外が発生するほとんどの場所が例外をキャッチしないことが例外ベースのエラー処理を適切に行うことが実際に非常に重要であるためです。例外をキャッチすることが適切な場所には3種類あります。他のすべての場所では、コードは例外を明示的に処理してはならず、deinit()クリーンアップ(つまりRAII)を行うために暗黙の呼び出しに依存deferする必要があります。(これはC ++の話が、基本的な原則はうまくとしてスウィフトの例外に適用されます。)多くのためexceptionsafecode.comを参照してください
bames53

しかし、関数をどのように実行しますか?私が@ bames53の方法で行くなら?
ファルハッド

1
@NickMoore Swiftの開発者が何と呼ぶか​​を選択しても、実際の違いはありません。Swiftの新しいエラー処理システムは、例外が実装されたものであり、この用語は業界全体で一般的に使用されています。
bames53 2015年

41

で宣言された関数をthrowsSwiftで呼び出すときは、関数呼び出しサイトにtryまたはで注釈を付ける必要がありますtry!。たとえば、スロー関数があるとします。

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

この関数は次のように呼び出すことができます:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

ここでは、で呼び出しに注釈を付けていますtry。これにより、この関数が例外をスローする可能性があり、後続のコード行が実行されない可能性があることをリーダーに呼び出します。また、この関数に注釈を付ける必要がありthrows、この機能はとき(すなわち、例外をスローする可能性があるため、willOnlyThrowIfTrue()スロー、その後、foo自動的に上向きに例外を再スローします。

スローする可能性があると宣言されているが、正しい入力を与えているためにスローされないことがわかっている関数を呼び出す場合は、を使用できますtry!

func bar() {
  try! willOnlyThrowIfTrue(false)
}

このように、コードがスローしないことを保証する場合、例外の伝播を無効にするために追加のボイラープレートコードを配置する必要はありません。

try! 実行時に適用されます。 try!して関数がスローする場合、プログラムの実行は実行時エラーで終了します。

ほとんどの例外処理コードは上記のようになります。例外が発生したときに例外を上方に伝搬するか、例外を除外するような条件を設定します。コード内の他のリソースのクリーンアップは、オブジェクトの破棄(つまりdeinit())、またはときどきdeferedコードを介して行う必要があります。

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

なんらかの理由で、実行する必要があるがdeinit()関数内にないコードをクリーンアップしている場合は、を使用できますdefer

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

例外を除いて、取引は、単に彼らが経て途中でクリーンアップをやって、発信者に上向きに伝播していることをほとんどのコードdeinit()またはdeferます。これは、ほとんどのコードがエラーの処理方法を知らないためです。何が問題だったかはわかっていますが、エラーについて何をすべきかを知るために、より高いレベルのコードが何をしようとしているかについては十分な情報がありません。ユーザーにダイアログを表示することが適切かどうか、再試行する必要があるかどうか、または他に何か適切かどうかはわかりません。

ただし、上位レベルのコードは、エラーが発生した場合の対処法を正確に把握している必要があります。そのため、例外を使用すると、特定のエラーが最初に発生した場所から処理可能な場所に発生します。

例外の処理はcatchステートメントを介して行われます。

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

複数のcatchステートメントを使用して、それぞれが異なる種類の例外をキャッチすることができます。

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

例外のあるベストプラクティスの詳細については、http://exceptionsafecode.com/を参照してください。これは特にC ++を対象としていますが、Swift例外モデルを調べた結果、基本はSwiftにも適用されると思います。

Swift構文とエラー処理モデルの詳細については、 『The Swift Programming Language(Swift 2 Prerelease)』を参照してください。


基本的にそれ自体をキャッチしてエラーを処理できますか?または入力機能
Farhad

1
@BrianS特に「入力関数」に関して、あなたが何を求めているのか正確にはわかりませんが、「キャッチ」は、例外のコンテキストでは、本質的に「ハンドル」の同義語です。つまり、プログラミング言語に関する限り、例外のキャッチと処理は同じことです。
bames53 2015年

静かに理解できないエラーが1つあります。助けていただけますか?Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad

@BrianS間違った署名のある関数をどこかで使用しているようです。何かNSData?, NSURLResponse?, NSError?が引数として受け取る関数を与えられることを期待していますが、引数を取らない関数を与えています。
bames53 2015年

または、何かが例外をスローするように宣言されていない関数を予期していて、例外をスローする関数をそれに与えている。
bames53 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.