Swift 5.3 日本語化計画 : Swift 5.3
エラー処理
エラー処理 は、プログラム内でのエラー状態にに応答し、回復するプロセスです。Swift は、throw、catch、propagate、および実行時での回復可能なエラーを操作するための第一級のサポートを提供しています。
一部の操作は、常に完全な実行、または有用な出力を生成する事をを保証しません。Optionals は値が存在しないことを表すために使用されますが、操作が失敗したときに、あなたのコードがそれに従って応答できるように、失敗の原因を理解するのにそれは便利です。
例としては、ディスク上のファイルからのデータの読み込みと処理のタスクを考えてみて下さい。ファイルが指定されたパスに存在しない、ファイルが読み取り権限を持っていない、またはファイルが互換性のある形式でコード化されていない事を含めて、このタスクが失敗する多くの場合があります。これらのさまざまな状況を区別する事は、いくつかのエラーを解決するために、ユーザーにプログラムが解決できないいくつかのエラーを通信できるようにします。
エラーの表現と Throw
Swift では、エラーは、Error プロトコルに準拠する型の値によって表されます。この空のプロトコルは、型がエラー処理のために使用できることを示しています。
Swift の列挙型は、通信されるエラーの性質に関する追加情報を可能にする、関連する値と関連するエラー条件のグループを、モデル化するのに特に適しています。たとえば、ゲーム内の自動販売機を操作するのにエラー状態を表す方法は以下のとおりです。
- enum VendingMachineError: Error {
- case invalidSelection
- case insufficientFunds(coinsNeeded: Int)
- case outOfStock
- }
エラーを throw すると、予期しない何かが起こり、実行の通常の流れが継続できないことをあなたは示すことができます。エラーを throw するには throw 文を使用して下さい。たとえば、以下のコードでは、5 つの追加のコインが自動販売機で必要とされていることを示すためにエラーを throw します。
エラーの処理
エラーが throw されると、コードの周囲のある部分は、例えば、問題を修正する事によって、別のアプローチを試みて、失敗したユーザに知らせることによって、エラーを処理するための責任を負わなければなりません。
Swift にはエラーを処理するため 4 つの方法があります。do-catch 文を使用してエラーを処理し、その関数を呼び出すコードに関数からエラーを伝播でき、optional の値としてエラーを処理し、またはエラーが発生しないことを主張する (assert) ことができます。各アプローチは、以下のセクションで説明します。
関数がエラーを throw した場合、それはあなたのプログラムの流れを変えるので、あなたがすぐにエラーを throw できる、コード内の場所を識別できることが重要です。あなたのコード内でこれらの場所を識別するには、エラーを throw できる関数、メソッド、またはイニシャライザを呼び出すコードの一部の前に try キーワードまたは try? や try! バリエーションを書きます。これらのキーワードは、以下のセクションで説明します。
throw 関数を使用したエラーの伝播
関数、メソッド、またはイニシャライザがエラーを throw できることを示すには、その関数の宣言内に、そのパラメータの後に throws キーワードを書いてください。throws でマークされた関数は、throwing する関数 と呼ばれます。関数が、戻り値の型を指定する場合は、戻り矢印(->)の前に throws キーワードを書いて下さい。
- func canThrowErrors() throws -> String
- func cannotThrowErrors() -> String
throwing する関数は、それを呼び出している所から範囲へと、その中に throw されるエラーを伝播します。
以下の例では、VendingMachine クラスには、要求された項目が使用できなければ、在庫切れのため、または現在入れた量を超えるコストの場合は、適切な VendingMachineError を throws する vend(itemNamed:) メソッドがあります。
- struct Item {
- var price: Int
- var count: Int
- }
- class VendingMachine {
- var inventory = [
- "Candy Bar": Item(price: 12, count: 7),
- "Chips": Item(price: 10, count: 4),
- "Pretzels": Item(price: 7, count: 11)
- ]
- var coinsDeposited = 0
- func vend(itemNamed name: String) throws {
- guard let item = inventory[name] else {
- throw VendingMachineError.invalidSelection
- }
- guard item.count > 0 else {
- throw VendingMachineError.outOfStock
- }
- guard item.price <= coinsDeposited else {
- throw VendingMachineError.insufficientFunds(coinsNeeded: item.price -
coinsDeposited) - }
- coinsDeposited -= item.price
- var newItem = item
- newItem.count -= 1
- inventory[name] = newItem
- print("Dispensing \(name)")
- }
- }
vend(itemNamed:) メソッドの実装は、早くメソッドを終了し、スナックを購入する要件のいずれかが満たされていない場合は、適切なエラーを throw するように guard 文を使用しています。throw 文はすぐにプログラム制御を転送するので、アイテムはこれらの要件のすべてを満たしている場合にのみ販売されます。
vend(itemNamed:) メソッドは、それが throws する全てのエラーを伝播するので、このメソッドを呼び出す全てのコードは do-catch 文、try? または try! を使用して、エラーを処理するか伝播し続けなければなりません。例えば、以下の例の buyFavoriteSnack(person:vendingMachine:) 関数も、throws する関数であり、 vend(itemNamed:) メソッドが throws した全てのエラーは buyFavoriteSnack(person:vendingMachine:) 関数が呼び出された時点まで伝播します。
- let favoriteSnacks = [
- "Alice": "Chips",
- "Bob": "Licorice",
- "Eve": "Pretzels",
- ]
- func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
- let snackName = favoriteSnacks[person] ?? "Candy Bar"
- try vendingMachine.vend(itemNamed: snackName)
- }
この例では、buyFavoriteSnack(person: vendingMachine:) 関数は、与えられた人のお気に入りのスナックを検索し、vend(itemNamed:) メソッドを呼び出すことによって、彼らのためにそれを購入しようとします。 vend(itemNamed:) メソッドはエラーを throw できるので、その前にある try キーワードで呼び出されます。
throw するイニシャライザは throw する関数と同じようにエラーを伝播できます。たとえば、下に挙げたリスト内の PurchasedSnack 構造体のイニシャライザは、初期化プロセスの一部として throw する関数を呼び出し、呼び出し元にそれらを伝播させることによって発生したエラーを処理します。
- struct PurchasedSnack {
- let name: String
- init(name: String, vendingMachine: VendingMachine) throws {
- try vendingMachine.vend(itemNamed: name)
- self.name = name
- }
- }
Do-Catch を使用したエラー処理
コードのブロックを実行して、エラーを処理するために、do-catch 文を使用して下さい。エラーが do 句内のコードによって throw された場合は、そのうちの一つがエラーを処理できるかどうかを決定するために catch 句と一致させられます。
ここで do-catch 文の一般的な形式を挙げます。
do {
} catch {
} catch where {
} catch , where {
} catch {
}
句が処理できるエラーを示すために、catch の後にパターンを書いて下さい。catch 句が、パターンを持っていない場合、句は任意のエラーに一致し、error という名のローカル定数にエラーを結合します。パターン一致の詳細については、パターン を参照してください。
例えば、以下のコードは VendingMachineError 列挙体の 3 つすべてのケースに対して一致します。
- var vendingMachine = VendingMachine()
- vendingMachine.coinsDeposited = 8
- do {
- try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
- print("Success! Yum.")
- } catch VendingMachineError.invalidSelection {
- print("Invalid Selection.")
- } catch VendingMachineError.outOfStock {
- print("Out of Stock.")
- } catch VendingMachineError.insufficientFunds(let coinsNeeded) {
- print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
- } catch {
- print("Unexpected error: \(error).")
- }
- // Prints "Insufficient funds. Please insert an additional 2 coins."
- func nourish(with item: String) throws {
- do {
- try vendingMachine.vend(itemNamed: item)
- } catch is VendingMachineError {
- print("Invalid selection, out of stock, or not enough money.")
- }
- }
- do {
- try nourish(with: "Beet-Flavored Chips")
- } catch {
- print("Unexpected non-vending-machine-related error: \(error)")
- }
- // Prints "Invalid selection, out of stock, or not enough money."
- func eat(item: String) throws {
- do {
- try vendingMachine.vend(itemNamed: item)
- } catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
- print("Invalid selection, out of stock, or not enough money.")
- }
- }
- func someThrowingFunction() throws -> Int {
- // ...
- }
- let x = try? someThrowingFunction()
- let y: Int?
- do {
- y = try someThrowingFunction()
- } catch {
y = nil- }
someThrowingFunction() がエラーを throws する場合、x と y の値は nil です。そうでなければ、x と y の値は、関数が返した値です。x と y は、someThrowingFunction() が返すどんな型の optional でもありうると言う事に注意して下さい。ここで、関数は整数を返し、x と y は optional の整数です。
try? を使うと、同じようにすべてのエラーを処理したいときに、簡潔なエラー処理コードを書くことができます。たとえば、以下のコードは、データを fetch するためにいくつかのアプローチを使用し、またはアプローチのすべてが失敗した場合は nil を返します。
- func fetchData() -> Data? {
- if let data = try? fetchDataFromDisk() { return data }
- if let data = try? fetchDataFromServer() { return data }
- return nil
- }
エラー伝播を無効に
時には、throw する関数やメソッドが、実際には、実行時にエラーを throw しないとわかるかも知れません。これらの場面では、エラーの伝播を無効にするために式の前に try! と書くことができるし、エラーが throw されない実行時のアサーションで呼び出しを包み込めます。エラーが実際に throw された場合は、実行時エラーが発生します。
たとえば、以下のコードは、与えられたパスにイメージリソースをロードするか、イメージがロードできない場合にはエラーを throws する loadImage(atPath:) 関数を使用しています。この場合、画像はアプリケーションと共に出荷されているため、実行時にエラーは throw されないので、エラーの伝播を無効にすることは適切です。
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
クリーンアップアクションの指定
コードの実行が現在のコードブロックを離れる直前に一連の文を実行するには、defer 文を使用して下さい。この文を使用すると、実行が現在のコードブロックを どのように 終了するか (エラーが throw されたために離れたのか、return や break などの文のために終了したのか)に関係なく、必要なクリーンアップを実行できます。たとえば、defer 文を使用して、ファイル記述子を確実に閉じ、手動で割り当てたメモリを確実に解放することができます。
現在の範囲が終了するまで defer 文は実行を延期 (defer) します。この文は、defer キーワードで構成され、文は、後で実行されます。延期された文は、エラーを throw するか、break または return 文のような文のうち、文の外に制御を移すことになる全てのコードを含めることはできません。延期されたアクションは、それらがあなたのソースで書かれた順序とは逆に実行されます。すなわち、最初の defer 文のコードは最後に実行され、第二のコードの defer 文は最後から 2 番目に実行され、という順序で実行されます。ソースコード順の最後の defer 文が最初に実行されます。
- func processFile(filename: String) throws {
- if exists(filename) {
- let file = open(filename)
- defer {
- close(file)
- }
- while let line = try file.readline() {
- // Work with the file.
- }
- // close(file) is called here, at the end of the scope.
- }
- }
上記の例では、open(_:) 関数には対応する close(_:) への呼び出しがあることを確認するために defer 文を使用しています。
注意: エラー処理コードが全く含まれなくても defer 文を使用できます。
前:Optional の連鎖 次:型キャスト
トップへ
トップへ
トップへ
トップへ
目次
Xcode の新機能
Swift について
Swift と Cocoa と Objective-C (obsolete)
Swift Blog より (obsolete)
Swift 5.3 全メニュー
- Swift へようこそ
- Swift について
- Swift 言語のガイド
- Swift の基本
- 基本演算子
- 文字列と文字
- コレクション型
- フロー制御
- 関数
- クロージャ
- 列挙型
- 構造体とクラス
- プロパティ
- メソッド
- サブスクリプト
- 継承
- 初期化
- デイニシャライザ
- Optional の連鎖
- エラー処理
- 型キャスト
- ネストした型
- 拡張機能
- プロトコル
- ジェネリック(汎用)
- 不透明な型
- 自動参照カウント
- メモリの安全性
- アクセス制御
- 高度な演算子
- 言語リファレンス
- マニュアルの変更履歴
トップへ
トップへ
トップへ
トップへ
トップへ
トップへ
トップへ
トップへ
トップへ
トップへ
上記の例では、buyFavoriteSnack(person:vendingMacine) 関数は、それがエラーを throw できるので、try 式で呼び出されます。エラーが throw された場合、実行は伝播を続行できるかどうかを決定する、catch 句にすぐに転送します。パターンが全く一致しない場合、エラーは最後の catch 句によってキャッチされ、ローカルの error 定数に結合されます。エラーが全く throw されない場合、do 文の残りの文が実行されます。
catch 節は、do 節内のコードが throw できるすべてのエラーを処理する必要はありません。いずれの catch 節もエラーを処理しない場合、エラーは周囲のスコープに伝播します。ただし、伝播されたエラーは周囲のスコープの どれか で処理されなければなりません。throw しない関数では、囲んでいる do-catch 節がエラーを処理しなければなりません。throw する関数では、囲んでいる do-catch 文か呼び出し側のどちらかがエラーを処理しなければなりません。エラーが処理されずに最上位のスコープに伝播した場合は、実行時エラーが発生します。
たとえば、上記の例では、VendingMachineError ではないエラーが代わりに呼び出し側の関数によって catch されるように書くことができます。
nourish(with:) 関数で、vend(itemNamed:) が VendingMachineError 列挙型の case の 1 つであるエラーを throws した場合、nourish(with:) はメッセージを印刷してエラーを処理します。そうでなければ、nourish(with:) はエラーをその呼び出し元のサイトに伝播します。その後、エラーは一般的な catch 節によって catch されます。
いくつかの関連するエラーを catch する別の方法は、catch の後にコンマで区切ってそれらをリストすることです。例えば:
eat(item:) 関数は、catch すべき自動販売機のエラーをリストし、そのエラーテキストはそのリストのアイテムに対応します。リストされている 3 つのエラーのいずれかが throw された場合、この catch 節は、メッセージを出力することによってそれらを処理します。後で追加されるかもしれない自動販売機のエラーを含め、その他のエラーは周囲のスコープに伝播されます。
エラー をOptional の値に変換
エラーを Optional の値に変換することによりそれを処理するのに try? を使用して下さい。try? 式の評価中にエラーが throw された場合は、式の値は nil になります。たとえば、以下のコードでは、x と y は同じ値であり、同じ動作をします: