エラーがスローされると、周囲のコードがそのエラーを処理する、例えば問題を修正、別の手段を試行、ユーザに失敗を通知などの責任を負います。
Swift にはエラーを処理する方法が 4 つあります。関数からその関数を呼び出したコードにエラーを伝播させる、do
–catch
文を使ってエラーを処理する、オプショナル値としてエラーを処理する、あるいはエラーが発生しないとアサートすることができます。各アプローチについて以降で説明しています。
関数がエラーをスローするとき、プログラムのフローが変わるため、エラーをスローするコードの場所をすばやく特定できることが重要です。コードの場所を特定するために、エラーをスローする可能性がある関数やメソッド、イニシャライザを呼び出すコードの前に、try
キーワード、あるいは try?
や try!
を記述します。これらのキーワードについて以降で説明しています。
try
, catch
, throw
キーワードを使用する他の言語での例外処理に似ています。Objective-C を含む多くの言語での例外処理と異なり、Swift のエラー処理は、処理コストが高いコールスタックを巻き戻す処理を伴いません。例として、throw
文の性能特性は、return
文と同等です。
スローする関数でエラーを伝播
関数やメソッド、イニシャライザがエラーを投げる可能性があることを示すために、関数の宣言でパラメータの後に throws
キーワードを記述します。throws
が付けられた関数をスローする関数と呼びます。関数が戻り値の型を指定している場合には、throws
キーワードをリターンアロー (->
) の前に記述します。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
スローする関数は、関数内でスローされたエラーを、関数が呼び出されたスコープに伝播します。
次の例は、リクエストされた商品が利用できない場合や、売り切れ、金額不足の場合に、適切な VendingMachineError
をスローする vend(itemNamed:)
メソッドを持つ VendingMachine
クラスです。
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 dispenseSnack(snack: String) {
print("Dispensing \(snack)")
}
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
dispenseSnack(name)
}
}
vend(itemNamed:)
メソッドの実装は、スナックを購入するための要件を満たしていない場合に、メソッドを早期に終了して適切なエラーをスローするために guard
文を使用しています。throw
文はプログラムの制御を即座に転送するため、すべての要件を満たしている場合にのみ商品は販売されることになります。
vend(itemNamed:)
メソッドはスローするエラーを伝播するため、このメソッドを呼び出すコードは do
–catch
文、あるいは try?
や try!
を使用してエラーを処理するか、エラーを伝播し続ける必要があります。例えば、次の例での buyFavoriteSnack(_:vendingMachine:)
もスローする関数で、vend(itemNamed:)
メソッドがスローするエラーは関数 buyFavoriteSnack(_: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(_:vendingMachine:)
は与えられた人のお気に入りのスナックを調べ、vend(itemNamed:)
メソッドを呼び出して購入しようとします。vend(itemNamed:)
メソッドはエラーをスローする可能性があるため、その前に try
キーワードを付けて呼び出されています。
スローするイニシャライザは、スローする関数と同じようにエラーを伝播することができます。例えば、次のコードでの構造体 PurchasedSnack
のイニシャライザは、初期化処理の一部としてスローする関数を呼び出し、イニシャライザの呼び出し元に伝播してエラーを処理します。
struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
do-catch でエラーを処理
コードブロックを実行してエラーを処理するために do
–catch
文を使用します。do
節のコードでエラーがスローされた場合、どの catch
節がエラーを処理できるか決定するために照合されます。
次は do
–catch
文の一般的な形式です。
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
}
catch
節が処理できるエラーを示すために catch
の後にパターンを記述します。catch
節にパターンが無い場合には、その節はどのエラーにもマッチし、ローカル定数名の error
にエラーをバインドします。パターンマッチについての詳細な情報は、Patterns を確認してください。
do
節のコードがスローする可能性があるすべてのエラーを、catch
節で処理する必要はありません。どの catch
節もエラーを処理しない場合、エラーは周囲のスコープに伝播します。しかしながら、エラーを処理する do
–catch
節に入れるか、スローする関数内にするかして、周囲のスコープでエラーを処理する必要があります。例えば、次のコードでは列挙型 VendingMachineError
の 3 つのケースすべてを処理していますが、他のすべてのエラーは周囲のスコープで処理する必要があります。
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} 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.")
}
// "Insufficient funds. Please insert an additional 2 coins." と出力
この例では、関数 buyFavoriteSnack(_:vendingMachine:)
がエラーをスローする可能性があるため、try
式で呼び出されています。エラーがスローされた場合、実行は即座に伝播し続けるかを決定する catch
節に転送されます。エラーがスローされない場合は、do
文の残りが実行されます。
エラーをオプショナル値に変換
エラーをオプショナル値に変換してエラーを処理するために try?
を使用します。try?
式を評価している間にエラーがスローされた場合、式の値は nil
になります。例として、次のコードの x
と y
は、同じ値で同じ振る舞いになります。
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
someThrowingFunction()
がエラーをスローする場合、x
と y
の値は nil
になります。そうでない場合には、x
と y
の値は関数が返した値になります。x
と y
は、someThrowingFunction()
が返す型のオプショナルであることに注目してください。関数が整数を返す場合には、x
と y
はオプショナルの整数となります。
try?
を使用することで、すべてのエラーを同じように処理したい場合に、簡潔なエラー処理コードを記述することができます。例として、次のコードはデータを取り出すためにいくつかの手段を使用し、すべての手段が失敗する場合には nil
を返します。
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
エラー伝播を停止
スローする関数やメソッドが、実際には実行時にエラーをスローしないことがわかっている場合があります。そうしたときには、エラー伝播を停止し、エラーがスローされないという実行時のアサーションで呼び出しをラップするために、式の前に try!
を記述することができます。エラーが実際にスローされた場合には、実行時エラーになります。
例として、次のコードは与えられたパスの画像リソースをロードし、画像をロードできない場合にはエラーを投げる関数 loadImage(_:)
を使用しています。このケースでは、画像がアプリケーションに搭載されているため、実行時にエラーがスローされることはなく、エラー伝播の停止に適しています。
let photo = try! loadImage("./Resources/John Appleseed.jpg")
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.