読者です 読者をやめる 読者になる 読者になる

理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

Blocks を使った再帰呼び出し

iOS

Objective-C には(正確には GCC、Clang の拡張として) Blocks という機能がある。
これは、C 及び C の拡張言語 (C++Objective-CObjective-C++) に対するクロージャ的な機能であるのだけれど、コードを書くときに嬉しいことの一つとして、いわゆる無名関数を記述できる、ということがある。特にフレームワークに対しコールバック関数を指定する場合、従来は関数ポインタ等で渡さざるを得なかったところに無名関数が渡せるようになることで、制御が非常に分かりやすくなる。

ところで、この Blocks で再帰呼び出しをしたいときにどうすれば良いのか。再帰呼出といえば階乗なので、早速書き下してみる。
以下のようなコードになるがこれはうまくいかない。factorial という blocks を定義するときに、未定義の blocks である factorial を呼び出しており、このまま実行すると EXC_BAD_ACCESS で落ちる。

#include <stdio.h>

int main(int argc, const char * argv[])
{
    int (^factorial)(int) = ^(int num) {
        return (num == 0)? 1 : num * factorial(num - 1);
    };
    
    printf("%d", factorial(5));  // 死ぬ
}

実行するためには、__block というストレージタイプを指定する必要がある。

int main(int argc, const char * argv[])
{
    __block int (^factorial)(int) = ^(int num) {
        return (num == 0)? 1 : num * factorial(num - 1);
    };
    
    printf("%d", factorial(5));  // 120
}

一般に Block の中で Block のスコープ外の変数を書き換えるときは __block を指定しなければならない (スコープ外の変数は基本的には readonly) のだけれど、__block を指定した変数は stack ではなく heap に取られる模様。