Upload
tomohiro-kumagai
View
3.001
Download
4
Embed Size (px)
Citation preview
EZ-‐‑‒NET 熊⾕谷友宏 http://ez-‐‑‒net.jp/
Swift 2.0 の
2015.06.20 @ 横浜へなちょこ iOS 勉強会 #35
ERROR HANDLING
熊谷友宏http://ez-net.jp/ @es_kumagai
Xcode 5 徹底解説
IP Phone 音でダイヤル 音で再配達ゴッドいつもの電卓 with 割勘ウォッチ
MOSA
Error Handling ってなに?
NSError を使いやすくするってコト!
エラーの話
これまでの NSErrorSwift 1.2
たとえば NSFileManager
これまでの NSErrorSwift 1.2
// オプショナルな NSError を状態として用意 var error:NSError? = nil
// 用意した NSError を inout で渡す let succeeded = fm.removeItemAtPath(path, error: &error)
// エラーを検査する if !succeeded, let error = error {
}
func removeItemAtPath( path:String, error: NSErrorPointer) -> BOOL
▶ 戻り値で目的の結果を返す ▶ エラーのときは
NSErrorPointer で詳細を返す ▶ つまりエラーの詳細を知りたければ
NSError? を事前に別途用意する
これまでの NSErrorSwift 1.2
func contentsOfDirectoryAtPath( path:String, error: NSErrorPointer) -> [AnyObject]?
▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーがあったときは
▶ 戻り値で nil を返す ▶ NSErrorPointer で詳細情報を返す
これまでの NSErrorSwift 1.2
これからの NSErrorSwift 2.0
これからの NSErrorSwift 2.0
// 正常処理のスコープを決める do {
// 目的をまっすぐ達成する try fm.removeItemAtPath(path) } catch let error as NSError {
// エラーならここで処理する }
func removeItemAtPath( path:String) throws -> Void
▶ 目的を遂行する ▶ エラーが発生するかもネ!
これからの NSErrorSwift 2.0
func contentsOfDirectoryAtPath( path:String) throws -> [String]
▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーが発生するかもネ!
これからの NSErrorSwift 2.0
正常系とエラー系とを分離Error Handling で
ところでこれまでの NSError は
これまでの NSErrorSwift 1.2
// NSError を用意しなくても実行可能 fm.removeItemAtPath(path, error: nil)
▶ 成否を戻り値だけで判定する ▶ 成功したものとして突き進むも可能
それって安全じゃない
Swift はそんなことはさせません
Swift は「安全」がお好き
たとえば
func setAttributes(attributes, ofItemAtPath:path) throws
Error HandlingSwift 2.0
fm.setAttributes(attr, ofItemAtPath:path) エラーを未想定ならエラー
Error HandlingSwift 2.0
try fm.setAttributes(attr, ofItemAtPath:path) エラーを想定!
Error HandlingSwift 2.0
try fm.setAttributes(attr, ofItemAtPath:path) エラーを想定!
正常系はこれ以降のスコープ全体
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
}
正常系のスコープを明記!
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
}
正常時の処理をこの中で決着する
正常系のスコープを明記!
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
} catch let error as NSError {
}
エラーが発生したらキャッチ!
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
} catch let error as NSError {
}
エラーが発生したらキャッチ!
エラー時の処理をこの中で決着する
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
} catch let error as NSError {
}
エラーが発生したらキャッチ!
エラー時の処理をこの中で決着する
エラー時はここは処理されない
まとめると
Error HandlingSwift 2.0
do {
try fm.setAttributes(attr, ofItemAtPath:path)
} catch let error as NSError {
}
エラーが発生したらキャッチ!
エラー時の処理をこの中で決着する
エラー時はここは処理されない
正常時の処理をこの中で決着する
正常系のスコープを明記!
エラーを想定!
つまり
エラーを確実に扱えるってコト!
next
絶対エラーにならなくない?
絶対にエラーにならない場合だってあるかもしれない
これまでの NSErrorSwift 1.2
これまでの NSErrorSwift 1.2
// エラーチェックを記載しない fm.setAttributes(attr, ofItemAtPath:path, error:nil)
成否に関わらず以下が実行される
Error HandingSwift 2.0
Error HandlingSwift 2.0
// エラーはない ! と明記する try! fm.setAttributes(attr, ofItemAtPath:path)
エラーのときは以下に進まず 強制終了
つまり
無視するにも覚悟が要るってコト!
next
エラーのときの後始末
Error Handlingdo { let handle = try file.open()
try fm.setAttributes(attr, ofItemAtPath:path)
handle.close()
処理が終わったら閉じたい
} catch {
}
でもエラーが発生すると…
ここまでたどり着けない
Error Handlingdo { let handle = try file.open()
defer { handle.close() }
try fm.setAttributes(attr, ofItemAtPath:path)
} catch {
}
最後に処理したいことを先に書く
ここでエラーが発生しても…
ブロックを抜ける直前に処理される
処理が終わったら閉じたい
余談
もしも Swift の Error Handling が@try-catch-finally だったとしたら
もし try-finally だったとしたら…var stream:Stream? = nil @try { stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) } @catch let error { } @finally { stream?.close() }
初期化と後始末のスコープ分断を考慮して外側に定義
未初期化を考慮してオプショナルチェイニング
そもそも、どこでエラーが起こるの…?
つまり
こうではなくvar stream:Stream? = nil @try { stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) } @catch let error { } @finally { stream?.close() }
▶ 入れ物の事前準備が必要 ▶ 3つのブロックに着目 ▶ 流れよりも構文が主役 ▶ どこでエラーになるかが
コードから読めない
こうなるdo { let handle = try file.open()
defer { handle.close() }
try fm.setAttributes(attr, ofItemAtPath:path)
} catch {
}
▶ 事前準備が不要 ▶ 流れが主役
原則成功、ときどき失敗 ▶ コードからエラーが
発生する箇所がわかる
Error Handling は美しいってコト!
next
Swift でエラーを扱う方法
Optional<T>
Optional<T>値があるかないかを扱う型
if let value = optional {
} else { // 値がなかったときにエラーとするかは状況次第 }
▶ 値の有無による判断を強制 ▶ 値がないときがエラーとは限らない
Optional のイメージ
?
Optional<T>使いどころ
// 例えば、リストの中から値を検索する関数 func find(list, value) -> Index? {
}
▶ 単純に値の有無を提示する ▶ エラーかどうかを決め付けない
いわゆる Either 型
いわゆる Either 型どちらかの状況を表現する型
enum Result<T> {
case Success(T) case Failure(Error) }
▶ 成功値か失敗値かを取る列挙型 ▶ 成否というより状況の切り分けに着目
いわゆる Either 型どちらかの状況を表現する型
switch getResult() {
case .Success(let value):
case .Failure(let error):
}
▶ 戻り値ひとつで状況の切り分けが可能 ▶ 両者を同じ重みで扱う
Either 型のイメージ
いわゆる Either 型使いどころ
▶ 戻り値で二つの場面を提示する ▶ 背反する分岐点を表現する
// 成功したか失敗したかで進路を分岐する
enum Result<T,U> {
case Succeeded<T> case Failed<U> }
switch getResult() {
case .Success(let value):
case .Failure(let error):
}
Fatal Error
Fatal Error致命的なエラー
fatalError("もうムリ…")
▶ 処理が継続できない状況を表現 ▶ 想定外を持ち越さない
以降の処理は実行させない!
Fatal Error のイメージ
Fatal Error使いどころ
▶ Optional で絶対に値が入っているとき ▶ Optional で値が入っていないと困るとき ▶ try が絶対にエラーにならないとき ▶ 処理を継続できないと判断したとき ▶ 相手に責任を取らせたいとき
などなど
活用の場面は幅広い
実際
Fatal ErrorSwift でも積極的に使われている
// 強制アンラップ let value = optional!
// 暗黙アンラップなオプショナル var value:String!
値がなければ fatalError
nil が入っているのに操作したら fatalError
Fatal ErrorSwift でも積極的に使われている
// 強制キャスト let subObj = obj as! SubClass
// エラーを想定しない try! execute()
キャストできなければ fatalError
エラーが起これば fatalError
Fatal Error で想定外を想定内へ転換する
Swift の Error HandlingNew!
エラー型の定義
Error Handling
protocol ErrorType {
}
▶ エラー型を ErrorType で表現 ▶ 実装を求められないプロトコル ▶ 列挙型と NSError が準拠できる
ErrorType プロトコル
Error Handling
enum OpenError : ErrorType {
case NotFound case Readonly case Busy(reason:String) }
▶ エラー型は 列挙型 で表現 ▶ 列挙型を ErrorType に準拠させる ▶ 起こり得るエラーを列記
値付き列挙子も使える
Error 型
関数やメソッドで使う
Error Handling
func open(file:FILE) throws -> Stream { : :
▶ エラーが有り得る機能に throws を付与 ▶ エラーになるかもしれないことが
プログラマーとコンパイラの両者に伝わる
エラーを示唆する
Error Handling
func open(file:FILE) throws -> Stream {
guard _exists(file) else {
throw OpenError.NotFound } : :
▶ エラーは throw で通知する ▶ エラーは列挙子で指定する ▶ throws を指定した機能でだけ通知可能
エラーを通知する
確実にエラーを想定使う側は
Error Handling
do { let stream = try open(file)
} catch { }
▶ 正常処理の範囲を do で表現 ▶ エラーが発生し得る場所に try を明記 ▶ エラーは catch で補足する
エラーに挑む!
エラーを想定する
Error Handling
do { let stream = try open(file)
} catch OpenError.NotFound {
} catch OpenError.ReadOnly {
} catch OpenError.Busy(let reason) {
}
全てのエラーに対処する
Error Handling
do { let stream = try open(file)
} catch OpenError.Busy {
} catch is OpenError {
}
値付き列挙子の値を加味しないことも可能
列挙子を加味せずに捕獲することも可能
全てのエラーに対処する
おさらい
Error Handling
▶ エラーを列挙型で表現起こり得るエラーが一目瞭然
▶ エラーの可能性を throws で示唆プログラマーとコンパイラに意思が伝わる
▶ エラーが起こり得る箇所に try を明記 どこでエラーを想定しているかが明確
▶ エラーを想定したコードが必須強制されると悩まずに済むので楽になる
おさらい
つまり
Error Handling でとっても楽になるってコト!
Error Handling のイメージ
Error Handling使いどころ
▶ 達成すべき目的がありそれを達成できない可能性があるとき
▶ 原因が実行時エラーに限られるとき ▶ 原因がいくつか考えられるとき ▶ 原因を提示し、対応を求めたいとき
// 目的が明確で、エラーも有り得る複合的な機能 func open(file:FILE) throws -> Stream
まとめ
エラーを扱う手段
Optional 型▶ 単純に値の有無を提示?いわゆる Either 型▶ 戻り値で分岐点を提示
Fatal Error▶ 強制終了して根本的な改善を迫る
Error Handling▶ 目的を遂行できない時に原因を提示
Swift はこれらの使用を強要する
?
強要されるとプログラミングが楽になる
つまり
Swift は すごいってコト!
おしまい。
▶ Error Handling ってなに? ▶ NSError を使いやすくするってコト! ▶ エラーを確実に扱えるってコト! ▶ 無視するにも覚悟が要るってコト! ▶ Error Handling は美しいってコト! ▶ Error Handling でとっても楽になるってコト! ▶ Swift はすごいってコト!
おまけ
Objective-C のこともSwift は見捨てない
Objective-C からの自動変換
Objective-C からの自動変換
// このような Objective-C コードが - (NSString*)getNameFromPath:(NSString*)path error:(NSError**)error;
// このような Swift コードになる func getNameFromPath(path:String!) throws -> String
▶ 戻り値がクラスの場合 ▶ 最後の引数が NSError** の場合
末尾の NSError を throws に変換
Objective-C からの自動変換
// このような Objective-C コードが - (BOOL)prepareWithOptions:(NSArray*)opts error:(NSError**)error;
// このような Swift コードになる func prepareWithOptions(opts:[AnyObject]!) throws -> Void
▶ 戻り値が BOOL の場合 ▶ 最後の引数が NSError** の場合
末尾の NSError を throws に変換
Objective-C への自動変換
Objective-C への自動変換
// このような Swift コードが func getName(path:String) throws -> String
// このような Objective-C コードになる - (NSString*)getName:(NSString*)path error:(NSError**)error;
▶ 戻り値が @objc 互換オブジェクトの場合 ▶ throws が指定されている場合
throws を NSError に変換
Objective-C への自動変換
// このような Swift コードが func prepare(options:[String]?) throws
// このような Objective-C コードになる - (BOOL)prepare:(NSArray**)options error:(NSError**)error;
▶ 戻り値が Void の場合 ▶ throws が指定されている場合
throws を NSError に変換
安心してError Handling を活用できる
つまり
Swift は かっこいいってコト!
おしまい。