Swiftのautoreleasepool
は引数として関数を受け取るもので、Objective-Cと似たような構文で記述することができます。
しかしこれには罠があり、今までObjective-Cで書いていた時と同じような感覚で使ってしまうと実行結果が異なる場合があります。
以下、再現を目的としたサンプルコードです。
@implementation Hoge
{
int _counter;
}
- (void)test
{
_counter = 0;
for (int i = 0; i < 10; i++) {
@autoreleasepool {
if (i == 5) {
return;
}
_counter++;
}
}
}
- (void)start
{
[self test];
NSLog(@"%d", _counter); // 5
}
class Hoge {
var _counter = 0
func test() {
_counter = 0
for i in 0..<10 {
autoreleasepool {
if i == 5 {
return
}
self._counter++
}
}
}
func start() {
test()
println(_counter) // 9
}
}
autoreleasepool
の中で特定の条件の時にreturn
で関数から抜けようというものです。
Objective-C版は5, Swift版は9と、コードの見た目は似ているのに結果が異なります。
気がついてしまえば簡単なのですが、Swiftのautoreleasepoolに書かれたreturn
はtest関数から抜けるわけではなく、引数に渡した内部関数(*用語正しいか判りません)から抜けるだけです。
当然、break
で外部のfor
ループから抜ける事もできません(上の例ではコンパイルエラーになります)。
また、Swiftではメンバ変数にアクセスする際にわざわざself.
を付ける必要が有るのも違いと言えます(これが結構面倒になりそうな予感ですが、だったら関数ごと外に出してしまった方が良いかもしれませんね)。
おまけ
@synchronized
に関しても、Swiftのautoreleasepool
関数のように、なんとなくObjective-Cと同様に書くことができるようです(こちらは非公式ですが)。
// どこかに用意しておく
func synchronized(obj: AnyObject, code: () -> ()) {
objc_sync_enter(obj)
code()
objc_sync_exit(obj)
}
...
// このように使う
synchronized (self) {
// ここに排他処理を書く
}
当然ながらautoreleasepool
同様の問題がこちらでも起きます。
こちらのなんちゃってsynchronized
関数は、ベストな選択とは言えないかもしれませんね。