Objective-Cのブロック(Blocks)に関するメモ
関数が第一級じゃない言語とかやってらんないわー、これだから静的言語は云々、というLL厨に対抗してか、最近は静的な言語でも第一級関数を取り入れようとあの手この手でグロい構文を導入してきています。Objective-Cもその例に違わずブロック(Blocks)という無名関数っぽい文法をiOS4.0あたりで取り入れたそうです。
とりあえず使ってみる
#import <Foundation/Foundation.h> int main() { typedef int(^calc_t)(int, int); NSArray *calcs = [NSArray arrayWithObjects: ^(int x, int y) { return x + y; }, ^(int x, int y) { return x - y; }, ^(int x, int y) { return x * y; }, ^(int x, int y) { return x / y; }, nil ]; for (calc_t calc in calcs) printf("%d\n", calc(3, 3)); return 0; }
$ gcc main.m -framework Foundation $ ./a.out 6 0 9 1
宣言の仕方はポインタの*
を^
にした感じですね。引数にラベルを付ける意味が皆無だからか、Objective-CではなくCっぽいスタイルです。
クロージャは出来るの…か?
__block
を使うとブロック外の変数を参照できるようになるようですが、果たして…。
#import <Foundation/Foundation.h> typedef int(^counter_t)(); counter_t generateCounter() { __block int count = 0; return ^(){ return count++; }; } int main() { counter_t counter = generateCounter(); printf("%d %d %d\n", counter(), counter(), counter()); return 0; }
$ gcc main.m -framework Foundation main.m:8:10: error: returning block that lives on the local stack return ^(){ return count++; }; ^~~~~~~~~~~~~~~~~~~~~~ 1 error generated.
う〜む。「ブロックはスタックに積まれるから、関数から出たときに破棄されるので、この書き方は出来ませんよ。」ということでしょうか。
ということを鑑みてか、ブロックをコピーできるBlock_copy
という何とも言えない命名センスの関数があるようです。これを使うとスタック上のブロックをヒープへ移動してくれるようなのです。が、逆に言えばヒープの解放Block_release
を自力で呼ばなければなりません。
#import <Foundation/Foundation.h> typedef int(^counter_t)(); counter_t generateCounter() { __block int count = 0; return Block_copy(^(){ return count++; }); } int main() { counter_t counter = generateCounter(); printf("%d %d %d\n", counter(), counter(), counter()); Block_release(counter); return 0; }
$ gcc main.m -framework Foundation $ ./a.out 0 1 2
ARCが絡むと…
今やデフォルトでmainの中に@autoreleasepool
が自動生成される時代になり*1、ARCに関する理解はiOS開発者に必須のものとなっていますが、そんな前置きは置いといて本題ですが、とりあえず「blocksは外の変数をstrongで参照する」ということを抑えておけば良さそうです。__block __weak Foo* _self = self;
のようにすればweakで参照できるようです*2。
参考
- 逆引きObjective-C for iPhoneアプリ - ブロック構文の基本的な使い方
- C/Objective-C + Blocks でクロージャ - TrashSUITE
- objective c - Returning block that lives on the local stack - Stack Overflow
- Objective-C - blocksの落とし穴 - Qiita [キータ]
- [iOS5] ARC (Automatic Reference Counting) : Overview - iOS 開発ブログ Natsu's note
- [iOS5] ARC : Outletにはweakプロパティを使おう - iOS 開発ブログ Natsu's note