1. ホーム
  2. swift

[解決済み] Swift言語でのエラーハンドリング

2022-04-23 22:52:57

質問

Swiftについてあまり詳しく読んでいないのですが、ひとつ気づいたのは、例外がないということです。 では、Swiftでエラー処理をどのように行っているのでしょうか?誰かエラー処理に関連するものを見つけたことがありますか?

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

Swift 2 & 3

Swift 2では、新しいエラー処理のメカニズムがあり、例外に多少似ていますが、細部は異なっています。

1. エラーの可能性を示す

関数/メソッドがエラーを投げる可能性があることを示したい場合、その関数/メソッドに以下の内容を含める必要があります。 throws キーワードは次のようになります。

func summonDefaultDragon() throws -> Dragon

注意:この関数が実際に投げることのできるエラーの種類は指定されていません。この宣言は、関数が ErrorType を実装した任意の型のインスタンスを投げることができる、あるいは全く投げないということを単に述べているに過ぎません。

2. エラーを投げる可能性のある関数の起動

関数を呼び出すには、次のように try キーワードを使用する必要があります。

try summonDefaultDragon()

この行は通常、次のようなdo-catchブロックに存在する必要があります。

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

注:catch節はSwiftのパターンマッチのすべての強力な機能を使用するので、ここでは非常に柔軟性があります。

でマークされた関数からスローイング関数を呼び出している場合、エラーを伝播させることを決定することができます。 throws キーワードを使用します。

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

また、スローイング関数を呼び出すには try? :

let dragonOrNil = try? summonDefaultDragon()

この方法では、何らかのエラーが発生した場合、戻り値かnilを取得します。この方法では、エラーオブジェクトを取得することはできません。

ということは try? のような便利な文があります。

if let dragon = try? summonDefaultDragon()

または

guard let dragon = try? summonDefaultDragon() else { ... }

最後に、そのエラーが実際に発生しないことが分かっている場合(例えば、前提条件を既にチェックしている場合など)には、次のようにします。 try! というキーワードがあります。

let dragon = try! summonDefaultDragon()

もしこの関数が実際にエラーを投げたら、アプリケーションにランタイムエラーが発生し、アプリケーションは終了してしまいます。

3. エラーのスローイング

エラーを投げるには、throw キーワードを次のように使用します。

throw DragonError.dragonIsMissing

に適合するものであれば何でも投げることができます。 ErrorType プロトコルを使用します。まず始めに NSError はこのプロトコルに準拠していますが、おそらくは列挙型の ErrorType これにより、関連する複数のエラーをグループ化することができ、さらに次のようなデータを追加することもできます。

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}


Swift 2 & 3 の新しいエラー機構と Java/C#/C++ スタイルの例外の主な違いは以下の通りです。

  • 構文が少し違います。 do-catch + try + defer vs トラディショナル try-catch-finally の構文があります。
  • 例外処理では、通常、成功パスよりも例外パスの方がはるかに高い実行時間が発生します。これは Swift 2.0 のエラーの場合ではなく、成功パスとエラーパスはほぼ同じコストです。
  • 例外はどこからでも投げられる可能性がありますが、エラーを投げるコードはすべて宣言されていなければなりません。すべてのエラーは、Javaの命名法では"checked exceptions"です。しかし、Javaとは対照的に、潜在的にスローされるエラーを指定することはありません。
  • Swiftの例外は、ObjCの例外と互換性がありません。あなたの do-catch ブロックはNSExceptionをキャッチしませんし、その逆も同様です。
  • Swiftの例外はCocoaと互換性がある NSError のどちらかを返すというメソッド規則があります。 false (の場合 Bool を返す関数)または nil (の場合 AnyObject を返す関数)、および NSErrorPointer をエラーの詳細と一緒に表示します。

エラー処理を容易にするための構文上の補助として、さらに2つの概念があります。

  • ディファレンシャルアクション defer キーワード) で、Java/C#/etc の finally ブロックと同じ効果を得ることができます。
  • ガード文 guard キーワード)を使うことで、通常のエラーチェックやシグナリングのコードよりもif/elseのコードを少なく書くことができます。

スウィフト1

ランタイムエラーです。

Leandrosが提案するように、実行時エラー(ネットワーク接続の問題、データの解析、ファイルのオープンなど)の処理には、次のようにします。 NSError ObjCでやったように、Foundation、AppKit、UIKitなどがこの方法でエラーを報告するからです。というのも、Foundation、AppKit、UIKitなどはこの方法でエラーを報告するからです。つまり、言語というよりフレームワークの問題です。

また、AFNetworkingのようなセパレータの成功/失敗のブロックも頻繁に使われるパターンです。

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

それでも頻繁に受信される障害ブロック NSError インスタンスで、エラーを記述しています。

プログラマーのエラー。

プログラマエラー(配列要素の境界外アクセス、関数呼び出しに渡された無効な引数など)については、ObjCで例外を使用していましたね。Swift言語では、例外の言語サポートはないようです(例えば throw , catch などのキーワード)。しかし、ドキュメントにあるように、これはObjCと同じランタイムで実行されているため、ObjCと同じように NSExceptions このように

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

ObjCコードで例外をキャッチすることを選ぶかもしれませんが、純粋なSwiftでそれらをキャッチすることはできません。

問題は、プログラマーのエラーに対して例外を投げるべきか、それともAppleが言語ガイドで提案しているようにアサーションを使うべきかということです。