一時的にメモリの消費が増える部分で、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
}