LoginSignup
18
19

More than 5 years have passed since last update.

Swiftのautoreleasepool等で注意する点

Last updated at Posted at 2014-06-23

Swiftのautoreleasepoolは引数として関数を受け取るもので、Objective-Cと似たような構文で記述することができます。

しかしこれには罠があり、今まで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
}
Swift

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) {
    // ここに排他処理を書く
}

参考:http://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized

当然ながらautoreleasepool同様の問題がこちらでも起きます。
こちらのなんちゃってsynchronized関数は、ベストな選択とは言えないかもしれませんね。

18
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
19