JY-CONTENTS

JY

JY-CONTENTS
search

+

MENU

TAG:

【Swift】エラー処理 do-catch文

【Swift】エラー処理 do-catch文

(DATE)

-

2017.03.15

(CATEGORY)

-

基本的な構文

//エラーを列挙型で定義
enum SomeError: Error {
    case error1
    //列挙型のケースの連想値を利用することで、エラーに付随する情報を表現することもできます。
    case error2(reason: String)
}

do {
    //throw文によるエラーが発生する可能性のある処理
    throw SomeError.error2(reason: "何かがおかしいようです")//Errorプロトコルに準拠した値
} catch SomeError.error1 {
    //エラー処理
    print("error1")
} catch SomeError.error2(let reason) {
    //エラー処理
    print("error2: \(reason)")
} catch { //パターンの指定がないcatch節はすべてのエラーにマッチ
    //エラー処理
    print("Unknown error: \(error)")
}

//実行結果
//error2: 何かがおかしいようです

Errorプロトコル

throw文のエラーを表現する型は、Errorプロトコルに準拠している必要があります。
Errorプロトコルは準拠した型がエラーを表現する型として扱えることを示すためのプロトコルで、準拠するために必要な実装はありません。
Errorプロトコルに準拠する型は、列挙型として定義することが一般的です。

do-catch文

throw文によるエラーが発生する可能性のある処理をdo節に記述し、catch節にエラー処理を記述します。
throw文はエラーを発生させる文で、Errorプロトコルに準拠した値を使って「throw エラー値」のように書きます。
do節の実行中にthrow文によるエラーが発生すると、catch節に実行が移ります。
catch節は複数続けて書くことができ、最初にマッチしたcatch節のブロックのみが実行されます

throwsキーワードを使用しての構文

関数、イニシャライザ、クロージャの定義にthrowsキーワードを追加すると、それらの処理の中でdo-catch文を用いずにthrow文によるエラーを発生させることができます。

enum OperationError : Error {
    case overCapacity
}

//throwsキーワードを持つ関数
func sam(_ int: Int) throws -> Int {
    guard int < 10 else {
        //do-catch文を用いずにthrow文によるエラーを発生させる
        throw OperationError.overCapacity
    }
    return int
}

//関数の呼び出し時にdo-catch文を使用
do {
    try sam(11)
} catch OperationError.overCapacity {
    print("数値が大き過ぎます")
}

//実行結果
//数値が大き過ぎます

※)定義にthrowsキーワードを指定していない場合、do-catch文で囲まれていないthrow文によるエラーはコンパイルエラーとなります。

tryキーワード

throwsキーワードが指定された処理(関数)を呼び出すには、それらの処理の呼び出しの前にtryキーワードを追加してtry 関数名(引数)のように記述します。tryキーワードを用いた処理の呼び出しは、throw文と同様に、do-catch文のdo節とthrowsキーワードが指定された処理の内部のみで使用できます。

イニシャライザに使用

throwsキーワードをイニシャライザに使用することで、インスタンス化の途中に発生したエラーを呼び出し元に伝えることができます。
下記はイニシャライザに渡す引数の値が13以上19以下でなければSamError.overCapacityを発生させ、インスタンス化できないようにしています。

enum SamError: Error {
    case overCapacity(str:String)
}

struct Sam {
    let a: Int
    
    init(_ arg: Int) throws {
        guard case 13...19 = arg else {
            throw SamError.overCapacity(str: "数値が範囲外です")
        }
        
        self.a = arg
    }
}

do {
    try Sam(10)
} catch SamError.overCapacity(let str) {
    print(str)
}

//実行結果
//数値が範囲外です

クロージャがエラーを発生させる場合

enum CutomErr: Error {
    case err1(str:String)
}

func sam(a:Int, handler: (Int) throws -> Int){
    do {
        try handler(a)
    } catch CutomErr.err1(let a) {
        print(a)
    }catch{
        
    }
}

sam(a:2){b throws -> Int in
    if b<10 {
        throw CutomErr.err1(str: "値が小さいです。")
    }
    return b*10
}

//値が小さいです。

rethrowsキーワードを使用しての構文

関数やメソッドをrethrowsキーワードを指定して定義することで、引数のクロージャが発生させるエラーを関数の呼び出し元に伝播させることができます。
rethrowsキーワードを指定するには、関数やメソッドが少なくとも1つのエラーを発生させるクロージャを引数に取る必要があります。

enum SomeError: Error {
    case err1(str:String)
}

//関数はrethrowsキーワードを用いて定義されているので、
//クロージャが発生させるエラーの処理を関数の呼び出し元に任せることができます。
func samFunction(_ closure: () throws -> Void) rethrows {
    //エラーを発生させるクロージャを引数にとり実行
    try closure()
}

do {
    try samFunction{()->Void in
        throw SomeError.err1(str:"SomeError")
    }
} catch SomeError.err1(let str) {
    // 引数のクロージャが発生させるエラーを、関数の呼び出し元で処理
    print(str) // SomeError
}

関数内で引数のクロージャが発生させるエラーを処理

関数内で引数のクロージャが発生させるエラーを処理し、別のエラーを発生させることもできます。

enum SomeError: Error {
    case err1
    case err2
}

func samFunction(_ closure: () throws -> Void) rethrows {
    do {
        try closure() //エラー(SomeError.err2)が発生
    } catch {
        //上記でエラーが発生したので下記(エラーSomeError.err1が発生)が実行される
        throw SomeError.err1
    }
}

do {
    try samFunction{()->Void in
        throw SomeError.err2
    }
    //関数samFunctionを実行した結果エラー(SomeError.err1)が発生
} catch {
    //発生したエラーはSomeError.err1になる
    error //err1
}

※)ただし、引数のクロージャが発生させるエラー以外をもとにしたエラーを発生させることはできません。

try!キーワード

throwsキーワードが指定された処理(関数)であっても、特定の場面では絶対にエラーが発生しないとわかっていてるケースもあると思います。
その場合は、tryキーワードの代わりにtry!キーワードを使用することでエラーを無視できます。
tryキーワードとは異なり、try!キーワードはdo-catch文のdo節やthrowsキーワードが指定された処理の内部でなくても使用できます。

enum SamError : Error {
    case overCapacity
}

//戻り値がInt.maxを超えてしまう可能性があるため、超えてしまった場合はエラーを発生させる
func sam(_ int: Int) throws -> Int {
    guard int <= Int.max / 3 else {
        throw SamError.overCapacity
    }
    
    return int * 10
}

//引数に9を与えているためエラーが発生しないことは明らかなためtry!キーワードを使用して、
//エラー処理を実装せずに関数を呼び出しています。
let tripleOfInt = try! sam(9) // do-catch文なしで実行可能
print(tripleOfInt) //90

try?キーワード

throwsキーワードが指定された処理であっても、利用時にはエラーの詳細が不要な場合もあります。このようなケースではtry?キーワードを使用できます。
try?キーワードを付けてthrowsキーワードが指定された処理(関数)を呼び出すと関数の戻り値がOptional<Wrapped>型となります。

enum SamError : Error {
    case overCapacity
}

//戻り値がInt.maxを超えてしまう可能性があるため、超えてしまった場合はエラーを発生させる
func sam(_ int: Int) throws -> Int {
    guard int <= Int.max / 3 else {
        throw SamError.overCapacity
    }
    
    return int * 10
}

//引数に9を与えているためエラーが発生しないことは明らかなためtry?キーワードを使用して、
//エラー処理を実装せずに関数を呼び出しています。
if let val = try? sam(9){ // do-catch文なしで実行可能
    print(val) //90
}

//“関数の戻り値はOptional<Wrapped>型
print(try? sam(9)) //Optional(90)

イニシャライザに使用

エラーを発生させるイニシャライザに対しても利用できるので、失敗可能イニシャライザのように扱うこともできます。

enum SamError: Error {
    case outOfRange
}

struct Sam {
    let val: Int
    
    init(_ val: Int) throws {
        guard case 13...19 = val else {
            throw SamError.outOfRange
        }
        
        self.val = val
    }
}

if let a = try? Sam(17) {
    print(a) //Sam(val: 17)
}
enum CustomErr: Error{
    case err1(Int)
    case err2(Cerr)
}

struct Cerr{
    let a:String!
    
    init(_ a:String){
        self.a = a
    }
    
    func sam(){
        print(a)
    }
}

func sam(_ a:Int)throws ->Int{
    let b = Cerr("over")
    guard a<10 else {
        throw CustomErr.err2(b)
    }
    
    return a
}

do {
    try sam(15)
} catch CustomErr.err2(let val) {
    val.sam()
}

NEW TOPICS

/ ニュー & アップデート

SEE ALSO

/ 似た記事を見る

JY CONTENTS UNIQUE BLOG

search-menu search-menu