ブロックをプロパティとして扱う
Androidの無名クラス、WindowsPhoneのラムダ(デリゲート)に慣れているプログラマはiOSに転向すると最初はそのような抽象がなくて絶望し、その後iOS4以降はBlocksが使えることに歓喜する。
Blockは基本的にはファンクションへのポインタ型で実装されており(Delphiを思い出すな)一時変数やパラメタとして使うのが普通だが、ならばプロパティとしても使えるだろうと以下のように定義する。
BBButtonProxy.h
@interface BBButtonProxy : NSObject @property (strong, nonatomic) NSString *label; @property (strong, nonatomic) void (^action)(); //※1 〜 〜 { //OKボタンが押下された場合の処理 BBButtonProxy* yesItem = [[BBButtonProxy alloc] init]; yesItem.label = @"yes"; yesItem.action = ^ { [self clearOrders]; [self.tableView reloadData]; }; }
定義自体に問題はなく、動作もはするが、ブロックの中でselfを直接又は間接的に参照した瞬間に「循環参照 (Self Capture)」に陥り、互いのメモリが解放されずに残ってしまう。また、プロパティactionの属性もstrongなので、BBButtonProxyはブロック自体を保持する。
なのでプロパティactionはコピー属性(copy)として、ブロック中で参照されるselfは__weakで修飾した別な変数に代入したのちに使用する。
@interface BBButtonProxy : NSObject @property (strong, nonatomic) NSString *label; @property (copy, nonatomic) void (^action)();※2 //OKボタンが押下された場合の処理 __weak HogeController* weakRef = self; BBButtonProxy* yesItem = [[BBButtonProxy alloc] init]; yesItem.label = @"yes"; yesItem.action = ^ { [weakRef clearOrders]; [weakRef.tableView reloadData]; };
※1:typedefを使って予め型を定義しておくこともできるが、このように書くことでブロック名がプロパティ名になりObjective-Cぽくて好きだ。
※2:このように書くのが一般的らしいのだが、copy属性はリファレンスによると「copyオブジェクトを挿入できるオブジェクト型のみ有効」とある。Blockを扱う場合、Block_copyが動作していると思うのだが、まだ検証していないので懐疑的だ。