【Swift】do-catchとthrows文の使い方!エラーハンドリングのやり方
この記事からわかること
- Swiftのエラー処理(エラーハンドリング)の実装方法
- NSErrorクラスとErrorプロトコルの違い
- do-catch文の使い方
- throwとthrowsの違い
- deferステートメントとは
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
Swiftのエラーハンドリングの方法をまとめていきます。
Handling Cocoa Errors in Swift
Error Handling
エラーハンドリングとは?
そもそもエラーハンドリングとはプログラム実行中に発生したエラーを適切に対象する処理のことです。様々なプログラミング言語でも使用される事柄であり、エラー処理や例外処理とも呼ばれます。
もちろんSwiftでもエラーハンドリングが行えるようにさまざまなものが用意されています。ポイントになってくるのは以下の5つです。
- NSErrorクラス/Errorプロトコル
- throwステートメント/throwsキーワード
- do-catchステートメント
- tryキーワード
- deferステートメント
エラーの定義や発火、その後の処理方法などを上記のポイントを使い分けながら処理することができます。ちなみにステートメントは文とも呼ばれたりしています。
NSErrorクラス
Swiftでのエラー処理はErrorプロトコルとして定義されており、Objective-CのNSErrorクラスと互換性が保たれています。まずはNSErrorクラスを見てみます。
class NSError : NSObject
NSError
クラスはObjective-Cで使用されていたエラークラスでエラーに関する3つの情報を保持しています。
var code: Int // エラーコード
var domain: String // エラードメインを含む文字列
var userInfo: [String : Any] // ユーザー情報
Errorプロトコル
protocol Error : Sendable
Error
プロトコルはSwiftにおけるエラー情報管理するためのプロトコルです。定義するときはenum(列挙型)などに準拠させ発生しうるエラー内容を列挙しておきます。
enum conversionError: Error {
case failed // 変換失敗
case overflow // オーバーフロー
}
おすすめ記事:【Swift】enum(列挙型)の使い方!値型enumと関連型enumとは
NSErrorクラスとは互換性があるので、キャスト(型変換)することが可能です。
var error = ConversionError.overflow
let nserror = error as NSError
print(nserror.code) // 1
throwステートメント:エラーを投げる
Swiftではthrow
ステートメントを使ってエラーを投げることができます。
throw ConversionError.failed
throw
の後には発生させたいErrorオブジェクトを渡します。NSError
も投げることが可能です。
throw NSError(domain: String, code: Int, userInfo: nil)
throwsキーワード:エラー発生
エラーを発生させる処理が組み込まれた関数やメソッド、イニシャライザにはエラーをスローできることを示すためにthrows
キーワードをつける必要があります。これによりエラーをスコープ外に伝播させることができるようです。
func test() throws {
//
}
”Only throwing functions can propagate errors. Any errors thrown inside a nonthrowing function must be handled inside the function.”
訳:スロー関数のみがエラーを伝播できます。非スロー関数内でスローされたエラーは、関数内で処理する必要があります。
引用元
例えば以下は文字列形式で受け取った数字を数値に変換するメソッドです。変換が失敗した時と10以上の値を渡された時にエラーが発生するようになっています。
func conversionNum(numStr:String) throws -> Int? {
guard let num = Int(numStr) else {
throw ConversionError.failed
}
if num > 10 {
throw ConversionError.overflow
}
return num
}
do-catchステートメント
エラーの発生はdo-catch
ステートメントを使って捕捉します。doステートメントとcatchステートメントの2つで構成されており、do内でエラーがthrowされる可能性のあるメソッドを呼び出し、catch内でそのエラーを参照することができるようになります。
do {
// エラーがthrowされる可能性のあるメソッドの呼び出し
} catch {
// エラーの捕捉
print(error)
}
catchブロックではerror
パラメータが宣言されているので補足したエラーオブジェクトがerror
に格納されます。カスタムエラーの場合はキャストすることで特定のエラータイプにアクセスすることが可能になります。
tryキーワード
do内でエラーをthrowするメソッドを呼び出すにはtry
キーワードをつけます。またtry?/try!
を使用することでエラーを無視する(do-catchを使わない)ことも可能です。
try expression
try
エラーを捕捉したい場合はdo-catch
とtry
を使用して以下のように記述することでエラー処理を行うことができます。
do {
try conversionNum(numStr: "0")
} catch {
print("エラー発生")
}
try?
try?
を使用する場合はエラーが発生しても無視でき、コンパイルエラーが発生することはありません。
try? conversionNum(numStr: "0")
try!
try!
を使用する場合はエラーの発生を無視できますが、エラーが実際に発生した場合ランタイムエラーが発生してしまいます。なのでtry!
はエラーが確実に発生せず、エラーの伝播が必要ない時などに使用します。
try! conversionNum(numStr: "0")
全体のサンプルコード
import Foundation
enum ConversionError:Error{
case failed
case overflow
}
func conversionNum(numStr:String) throws -> Int? {
guard let num = Int(numStr) else {
throw ConversionError.failed
}
if num > 10 {
throw ConversionError.overflow
}
return num
}
do {
try conversionNum(numStr: "11")
} catch {
print("エラー発生")
}
deferステートメント
エラーが発生すると一連の流れの処理が中断され、最後に必ず実行したい処理があっても呼び出されないまま終了してしまいます。これを解決するのがdefer
ステートメントです。
defer
ステートメントは通常の処理の最後、またはエラーが発生した直後に実行される処理を定義できるステートメントです。do-catch
内で記述し、処理が終了またはエラーの発生したタイミングまで実行を延期します。後続で行いたい処理をキューに溜めていくイメージですかね?
do {
defer { print("処理1") }
try conversionNum(numStr: "11")
defer { print("処理2") }
} catch {
print("エラー発生")
}
例えば上記の場合エラーが発生しなければ「処理2処理1」が表示されますが、エラーが発生した場合は「処理1エラー発生」で終了します。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。