LoginSignup
174
167

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-10-15

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