ブロックの外で定義された変数をブロック内で使うとき、その変数はブロック内に 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 {} 《マクロ本体》` に展開される