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が逆なのでは
ご指摘ありがとうございます。
返信削除おっしゃる通り逆になっていましたので修正しました。