忘備録-Swiftのエラー処理


趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [詳解 Swift 第5版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X)


##エラーの発生
throw文が実行されることをエラーを投げるという。
throw文の式にはErrorプロトコルに適合した物を使う。
throw文を含む関数をエラー通報関数と呼び、throwsというキーワードをつける。

```swift
func hoge(b: Bool) throws{
  if b {
    print("hoge")
  } else {
    throw hogeError.unknown
  }
}

enum hogeError : Error {
  case unknown
}

do{
  try hoge(b: true)
} catch {
  //処理
}

```

##エラーへの対応方法の種類
１.try 
エラーが発生した場合上流の呼び出しものに伝播。その呼び出し以降の処理は実行されない。この呼び出しを含む関数やメソッドは、エラーを投げる関数として宣言しなければならない。上流処理にエラーを渡すだけ。

```swift
func fuga() throws{
  try hoge(b: true)    //エラーが来たらエラーを投げるだけ
}

func hoge(b: Bool) throws{
  if b {
    print("hoge")
  } else {
    throw hogeError.unknown
  }
}

enum hogeError : Error {
  case code(Int)
  case unknown
}

do{
  try fuga()
} catch {
  // 処理
}
```

２.do-catchとtry 
do-catch内のtryで起きたエラーをcatch文で補足する。この呼び出しを含む関数やメソッドは、エラーを投げる関数として宣言する必要はない。一布対処できないエラーについては外部へ伝搬させることも可能。

```swift
do catch{
    文
    try エラーを投げる関数
    文
} catch パターン where節 {
    文
} catch {
    文
}
```

３.try? 
この構文を使ってエラーを投げるとエラーが発生した場合はnilが返される。エラーが発生しなかった場合は通常の値が返される。なので返り値をオプショナル型として扱うことができる。 if let hoge = try? エラーを投げる関数{ 成功時処理 }のような書き方が可能。

４.try! 
エラーが発生すると、プログラムが停止する。。エラーが発生しなかった場合は通常の値が返される。使う場合は絶対にエラーが起きないことを確認して使う。



##defer文

そのコードブロックの実行が終了するか、中断されるときに必ず実行される処理を記述できる。
defer { 処理 }
ファイルのクローズ処理とかに使う。

 ```swift
func hoge(i: Int) -> Int{
  var msg = "hoge"
  var result = 0

  defer { print("1\(msg): \(result)")}
  

  msg = "fuga"
  defer { print("2\(msg): \(result)")}

  if i == 0 { return result}

  defer { print("3\(msg): \(result)")}
  msg = "piyo"
  
  result += 1
  return result
  
  defer { print("4\(msg): \(result)")}
  msg = "foo"

}
print("0-----------")
hoge(i: 0)
print("1-----------")
hoge(i: 1)
```

結果

```
0-----------
2fuga: 0
1fuga: 0
1-----------
3piyo: 1
2piyo: 1
1piyo: 1
```

・実行が及んでいないdefer文は実行されない
・defer文が複数あると後の処理から実行される
・defer文の変数は実行されているときに格納されている値が使われる
・defer文が実行されるのはreturnで返す値が決まってから


##アクセス制御
・open
そのモジュールの情報をインポートすればどんなソースファイルからでもアクセス可能。
クラス、およびクラスのメンバにのみ適用可能。サブクラス作成、上書き定義定義も可能。

・public
そのモジュールの情報をインポートすればどんなソースファイルからでもアクセス可能。
サブクラス作成、上書き定義定義はそのクラスを定義したモジュール内でのみ可能。

・internal
定義を含むソースモジュールと同じモジュールの内部からなら、どんなソースファイルからでもアクセス可能。
インポートの必要はない。
何も指定しない場合のアクセス制御はinternalになる。

・fileprivate
定義されたソースファイル内だけで利用可能。
他のソースファイルからはアクセスできない。

・private
クラスや構造体のような定義単位の内部だけで使用可能。

##クラスの継承とアクセス制御
サブクラスにはスーパークラスより広いアクセス権は指定できない。
一方メンバを上書き定義する場合は広いアクセス権を渡すことができる。しかし狭くはできない。
サブクラスのメソッドがスーパークラスのメソッドを利用して実装される場合は、スーパークラスのメソッドの可視性がサブクラス側の可視性に影響することはない。

##プロトコルとアクセス制御
プロトコル内の個々の宣言に対しては可視性を設定することはできない。
プロトコル定義に対しては指定でき、プロトコル全体も同じ可視性になる。

型定義において型よりも狭い範囲のプロトコルを採用することができるが、プロトコルに
アクセス可能な範囲のみで、プロトコルに適合した型であるとみなす。


##拡張定義とアクセス制御
拡張定義で新しいメンバを追加した時の可視性の既定値は本体のメンバーの既定値と同じになる.。

拡張定義extensionの前にアクセス制御の修飾子をつけることができる。ここでの指定は拡張定義内のメンバの可視化の規定値を変更する働きがある。

##プロパティとアクセス制御
プロパティや添字付けの定義も可視性の宣言を行うことができる。
セッタ(set)に対してはゲッタ(get)よりも低い可視性を与えることが可能。
セッタに対してだけ可視性を設定するためにopen(set),public(set)など修飾子が用意されている。

##アサーションのための関数
デバック用の機能
条件を満たせばプログラムを停止させる。
あるいは実行されたときにプログラムを停止させるものがある。
assertは-Oオプションで無効化できるがpreconditionはできない。
ログに書き出して停止させるfatalErrorというものもある。

|関数名|条件判定|使う時|
|:----------|:----------|--------------|
assert |可能|開発時のエラー発見
assertionFailure|不可|開発時のエラー発見
precondition|可能|開発時・運用時のエラー発見
preconditionFailure|不可|開発時・運用時のエラー発見
fatalError|不可|致命的エラー

##戻ってこない関数
assertionFailureやpreconditionFailureのようにプログラムを停止させて呼び出し側に制御が戻ってこない関数はNeverという。
特別な方を返す関数として定義する。

##利用可能な条件を指定する属性
IOS8以上で動作するとき@available(OSX 10.0 , iOS 8.0, *)を対象の処理の前に記述。