weakify/strongify マクロを使うと weak self パターンが簡単に書ける

  • 173
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

ブロックの外で定義された変数をブロック内で使うとき、その変数はブロック内に strong 参照でキャプチャされる。場合によってはこれが循環参照を引き起こすことがある:

// self が block を strong 参照→ block が self をキャプチャ(strong 参照)
self.aStrongProperty = ^{
    NSLog(@"self = %@", self);
};

これを回避するには、問題となる変数の weak 参照をブロックに渡すことになる:

__weak typeof(self) wself = self;
// __weak TheClass *wself = self; // If Xcode < 4.4

self.aStrongProperty = ^{
    NSLog(@"self = %@", wself);
};

libextobjc はこのパターンを簡単にする weakify/strongify マクロを提供している。ブロックの外で、 weak 参照にしたい変数を引数に @weakify() を呼び、ブロックの中で先ほどの変数を引数に @strongify() を呼べばよい:

#import <libextobjc/EXTScope.h>

...

- (void)weakSelfPattern {
    @weakify(self);
    self.aStrongProperty = ^{
        @strongify(self);
        NSLog(@"self = %@", self);
    };
}

マクロの引数には @weakify(var1, var2, var3) のように複数の変数を渡せる。また unsafe_unretained 参照を作る @unsafeify() マクロも用意されている。

wself = self パターンと異なり、同名の変数をそのまま使えるので、ブロック内で wself の代わりに self を使ってしまうミスは起こりえない。

蛇足:マクロ名が @ で始まっているが、これは Objective-C の組み込みのディレクティブではない。マクロ定義の先頭に空の autoreleasepool 文を置くことでディレクティブのような見た目のマクロを作るテクニックを使っている:

#define strongify(...) \
    autoreleasepool {} \
    《マクロ本体》

// `@strongify()` が `@autoreleasepool {} 《マクロ本体》` に展開される