1. ホーム
  2. swift

[解決済み] Swiftのdo-try-catch構文

2022-05-02 11:16:10

質問

swift 2の新しいエラー処理について理解するために試してみました。以下は、私が行ったことです。私は最初にエラー列挙型を宣言しました。

enum SandwichError: ErrorType {
    case NotMe
    case DoItYourself
}

そして、エラー(例外ではありません。エラーです)を投げるメソッドを宣言しました。以下がそのメソッドです。

func makeMeSandwich(names: [String: String]) throws -> String {
    guard let sandwich = names["sandwich"] else {
        throw SandwichError.NotMe
    }

    return sandwich
}

問題は呼び出し側からです。以下は、このメソッドを呼び出すコードです。

let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
}

の後に do 行のコンパイラは Errors thrown from here are not handled because the enclosing catch is not exhaustive . しかし、私の意見では、これは網羅的なものです。 SandwichError の列挙です。

通常のswitch文の場合、すべてのケースを処理すると網羅的であると理解できます。

どのように解決するのですか?

Swift 2のエラー処理モデルには、「網羅性」と「回復力」という2つの重要なポイントがあります。一緒に、それらはあなたの do / catch 文は、投げることができるとわかっているエラーだけでなく、起こりうるすべてのエラーをキャッチする必要があります。

関数が投げることができるエラーの種類を宣言していないこと、そして、全く投げることができないかどうかだけを宣言していることに注意してください。これは一種のゼロ・ワン・イン・フィニティの問題で、他の人(将来の自分も含めて)が使う関数を定義する者として、関数の実装が変更されるたびに、その関数のすべてのクライアントを適応させる必要はないでしょう(どのエラーを投げることができるかも含めて)。関数を呼び出すコードは、そのような変化に強いものであってほしいのです。

この関数は、どのようなエラーを投げるか (あるいは将来投げる可能性があるか) を知ることができないので catch ブロックは、どのような種類のエラーを投げるかを知りません。そこで、自分が知っているエラーの種類を処理するだけでなく、 知らないエラーの種類を処理するために、普遍的な catch そうすれば、将来この関数が投げるエラーのセットを変更したとしても、 呼び出し元はそのエラーを捕捉することができます。

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
    print("Not me error")
} catch SandwichError.DoItYourself {
    print("do it error")
} catch let error {
    print(error.localizedDescription)
}

しかし、そこで終わりではない。このレジリエンス(回復力)の考え方をもう少し考えてみましょう。あなたが設計したサンドイッチの方法では、エラーを使うすべての場所でエラーを記述しなければなりません。つまり、エラーケースのセットを変更するたびに、それを使っているすべての場所を変更しなければならないのです...あまり楽しいことではありませんね。

独自のエラータイプを定義することで、そのようなことを一元管理できるようにするのが狙いです。例えば description メソッドを使用します。

extension SandwichError: CustomStringConvertible {
    var description: String {
        switch self {
            case NotMe: return "Not me error"
            case DoItYourself: return "Try sudo"
        }
    }
}

そして、エラー処理のコードは、エラータイプに自分自身を記述するように求めることができます。これで、エラーを処理するすべての場所で同じコードを使用でき、将来起こりうるエラーケースも処理できるようになります。

do {
    let sandwich = try makeMeSandwich(kitchen)
    print("i eat it \(sandwich)")
} catch let error as SandwichError {
    print(error.description)
} catch {
    print("i dunno")
}

これはまた、エラータイプ (あるいはその拡張) が他のエラー報告方法をサポートする道を開くものでもあります。 UIAlertController iOSのユーザーにエラーを報告するためのものです。