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

  • 4
    Like
  • 0
    Comment
More than 1 year has passed since last update.

一時的にメモリの消費が増える部分で、autoreleaseを使うのに加え、do〜try〜catchでエラーハンドリングをするといった場面があって、

autoreleasepool {
    do {
        try process()
    } catch (let e) {
        //
    }
}

こうではなくて、、、

雑多な例ですが...

func doSomeTask() {
   do {
       for i in (0..<100) {
           autoreleasepool {
               // 大きい画像を扱うので結構メモリを消費するからautoreleasepoolを使いたい
               let largeImage = UIImage(contentsOfFile: "image\(i).png")
               // 何らかの処理
                try ~~~~~~~~ //これができない!! ★
            }
       }
   } catch (let e) {
       print(e) // ★の例外をここで拾いたい
   }
}

このように使いたい場合に、autoreleasepoolが例外をthrowできないので上記のように書くことができません。
なので、上記の例ができるように、以下の用に実装してみます。

実装してみる

public func autoreleasepool(@noescape code: () throws -> ()) rethrows {
    try {
        var error: ErrorType?
        autoreleasepool {
            do {
                try code()
            } catch (let e) {
                error = e
            }
        }
        if let error = error {
            throw error
        }
    }()
}

あまり綺麗に書けないですが、予めErrorTypeが入る変数をautoreleasepoolの外に宣言し、
内部でcode()を実行し、エラーをキャッチしたら先に用意した変数にいれ、それをautoreleasepoolを抜けたあとにthrowします。

同じ関数名ですが、エラーもなく宣言ができます。
これで、先ほどの例で、autoreleasepoolの前に、tryを付けてあげることで、実現することができます。

使用してみる


func doSomeTask() {
   do {
       for i in (0..<100) {
           try autoreleasepool {
               let largeImage = UIImage(contentsOfFile: "image\(i).png")
               // 何らかの処理
                try ~~~~~~~~ // ★
            }
       }
   } catch (let e) {
       print(e) // ★の例外をここで拾うことができる!
   }
}

何故同名の関数なのに問題ないのか

autoreleasepoolオリジナルの宣言を見てみると、

定義
public func autoreleasepool(@noescape code: () -> ())

となっていて、引数の型が

() -> () (オリジナル)
() throws -> () (今回実装したもの)
となっているので違う引数の型と判断され、結果としてオーバーロードした事になります。

autoreleasepoolに渡すクロージャーの内部でtryで例外を投げる可能性がある場合は、今回実装した方のautoreleasepoolが適応され、クロージャーの内部で例外が投げられることがない、通常の使い方の場合は、オリジナルの方が適応されます。


autoreleasepool {
    // 通常の autoreleasepool
}

try autoreleasepool {
    try ~~~~~
    // 例外をthrowできるautoreleasepool
}