Swiftのdo〜try〜catchとdeferの組み合わせ

Wednesday, February 10, 2016

現在PDFGeneratorの新規機能の追加だったり、より安全にPDFが出力できるように整えているのですが、
その中で、どうしても 例外 をdo〜try〜catchで処理しなくてはならなくて。

そんな時に、例えばPDFの描画を開始し、例外を返すかもしれないprocess()を実行するという流れがあった場合に、

do {
    UIGraphicsBeginPDFContextToFile(outputPath, CGRectZero, nil)
    try process()  
    UIGraphicsEndPDFContext()
} catch (let error) {
    print(error)
}

処理が成功する場合は、UIGraphicsEndPDFContext()が呼ばれるのですが、tryで例外がキャッチされた場合は、UIGraphicsEndPDFContext()がスキップされてしまいます。危ない…

そうならないために、以下のアプローチをとります。

UIGraphicsBeginPDFContextToFile(outputPath, CGRectZero, nil)
do {
    try process()  
} catch (let error) {
    print(error)
}
UIGraphicsEndPDFContext()

うーん、これでも間違ってはいないのですが、ちょっとスコープが広すぎる気がします。

そこで、deferを使って、doステートメントの中で収まるようにします。 deferは、そのスコープを抜けるときに中に書いた処理を実行してくれるものです。
今回のケースにはもってこいですね。

do {
    UIGraphicsBeginPDFContextToFile(outputPath, CGRectZero, nil)
        defer {
            UIGraphicsEndPDFContext()
        }
    try process()  
} catch (let error) {
    print(error)
}

なんとなく、終了の処理を、process()より先に書くのは抵抗がありますが、 これで、tryで仮に例外がキャッチされて、catchステートメントに移行したとしても、UIGraphicsEndPDFContext()が呼ばれます。

流れとしては、

  • 正常に処理される場合

    1. UIGraphicsBeginPDFContextToFile(outputPath, CGRectZero, nil)
    2. process()
    3. UIGraphicsEndPDFContext()
  • 例外がキャッチされる場合

    1. UIGraphicsBeginPDFContextToFile(outputPath, CGRectZero, nil)
    2. process()
    3. UIGraphicsEndPDFContext()
    4. print(error)

となるので、どちらの場合でも、処理が順序良く処理されるので、これで一安心です。

はじめこれに気が付かず、最初に出した例で処理を書いていたので、例外が発生した時におかしくなってようやく気付きました。。。

techSwiftiOStips

autoreleasepoolから例外を投げられるようにする

文字列中の数字を全角/半角変換する