ReactiveCocoaのメモリマネジメントについてのドキュメントでも触れられているライブラリlibextobjcの@strongifyと@weakifyについて紹介します。
なお、libextobjcの導入方法などについては以前の記事「CocoaPodsでReactiveCocoaをインストールする」を参照ください。
ARCとstrong reference cyclesについて
まずは、ARCとblockを使った場合によくはまりやすい問題について簡単におさらいします。
例えば、次の例では「self → someHandler → self」という強参照のループが発生してしまうので、selfが開放されなくなります。
self.someHandler = ^{
self.foobar = @"...";
};
この強参照ループを断ち切るためには、__weakなselfを作って、それを使うようにします。
__weak id weakSelf = self;
self.someHandler = ^{
weakSelf.foobar = @"...";
};
__weakなオブジェクトは開放済みの場合もあるので (そのときはnilになっている)、生存判定を追加すると次のようになります。
__weak id weakSelf = self;
self.someHandler = ^{
if (weakSelf) {
weakSelf.foobar = @"...";
}
};
さらに、block実行中にはオブジェクトを破棄されたくない場合には、block内で__strongしてやります。
__weak id weakSelf = self;
self.someHandler = ^{
__strong id strongSelf = weakSelf;
if (strongSelf) {
strongSelf.foobar = @"...";
// ほかいろいろな処理
}
};
詳細は、Apple公式ドキュメント「Transitioning to ARC Release Notes」の「Use Lifetime Qualifiers to Avoid Strong Reference Cycles」の節を参照ください。
@strongifyと@weakifyについて
さて本題である、libextobjcの@strongifyと@weakifyです。
これらを用いると、前節の最後のコードが次のように簡単に書けます。
@weakify(self);
self.someHandler = ^{
@strongify(self);
if (self) {
self.foobar = @"...";
// ほかいろいろな処理
}
};
@strongifyと@weakifyは簡単に言えば、マクロと変数のshadowingを利用して使いやすくしたもので、
だいたい次のコードと同じです (実際にはidではなく、__typeof__(self)によってちゃんとした型になります)。
__weak id self_weak_ = self;
self.someHandler = ^{
__strong id self = self_weak_;
if (self) {
self.foobar = @"...";
// ほかいろいろな処理
}
};
実装の詳細は、libextobjc内のEXTScope.hを参照してください。
ちなみに、@strongify(foo, bar, buz)のように複数書くことができるみたいです。
ReactiveCocoaの場合
ReactiveCocoaに話を戻すと、前回の記事でメモリリークすると言っていたコードは、
@strongifyと@weakifyを使うと次のように書けます。
@weakify(self)
[self.textField.rac_textSignal subscribeNext:^(NSString* x) {
@strongify(self)
if (x.length >= 3)
self.upperLabel.text = x.uppercaseString;
}];
ReactiveCocoaでは、@strongify(self);と@weakify(self);はよく使うことになるので、コードスニペットに登録しておくことをお勧めします。
おわりに
libextobjcの@strongifyと@weakifyについて簡単に紹介しました。


> これらを用いると、前節の最後のコードが次のように簡単に書けます。
返信削除の下の例、weakifyとstrongifyが逆なのでは
ご指摘ありがとうございます。
返信削除おっしゃる通り逆になっていましたので修正しました。