『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2 
2011年1月27日発売でも、すでに書店に並びつつある『
iOS 4プログラミングブック』。
今回も引き続き第5章マルチスレッドの補遺として、「__block修飾子」を深追いしてみます。
その1で書いたとおり、__block変数の実態は「total.__forwarding->total」て感じです。Block生成直後はstackに居るので、この
__forwardingポインタは自分が含まれる構造体を示し、Block_copyでstackからheapに移動されると(189ページ参照)、
__forwardingポインタが移動後のアドレスを示すわけですね。「total.__forwarding->total」は、stackにあるか、heapにあるか、気にせずにアクセスできるわけです。
確かめてみましょう!
#import <Foundation/Foundation.h>
#import <stdio.h>
extern const char *_Block_byref_dump(void *);
void dump(int line, int *p)
{
    p -= 4;
    printf("\ndump line:%d\n", line);
    puts(_Block_byref_dump(p));
}
int *test()
{
    __block int total = 11;
    dump(__LINE__, &total);
    void (^block_on_stack)() = ^{
        ++total;
        dump(__LINE__, &total);
    };
    block_on_stack();
    printf("\n___ Block_copy ___\n");
    void (^block_on_heap)() = Block_copy(block_on_stack);
    dump(__LINE__, &total);
    block_on_stack();
    block_on_heap();
    printf("\n___ Block_release ___\n");
    Block_release(block_on_heap);
    dump(__LINE__, &total);
    block_on_stack();
    return &total;
}
int main()
{
    dump(__LINE__, test());
}
__block変数をダンプするための秘密の関数 _Block_byref_dump を使って、それぞれの状況で__block変数の状態を表示してみます。このソースコードに埋めてみましょう。
int *test()
{
    __block int total = 11;
    dump(__LINE__, &total);
    
        dump line:17
        byref data block 0xbffffa70 contents:
          forwarding: 0xbffffa70    ← stackに生成されてる
          flags: 0x0
          size: 20
    
    void (^block_on_stack)() = ^{
        ++total;
        dump(__LINE__, &total);
    };
    block_on_stack();
    
        dump line:23
        byref data block 0xbffffa70 contents:
          forwarding: 0xbffffa70  ← stack上のBlockからstack上の__block変数を使用
          flags: 0x0
          size: 20
    
    printf("\n___ Block_copy ___\n");
    void (^block_on_heap)() = Block_copy(block_on_stack);
    dump(__LINE__, &total);
    
        dump line:31
        byref data block 0x100150 contents:
          forwarding: 0x100150    ← heapにコピーされた!
          flags: 0x1000002        ← 要Block_releaseフラグと、参照カウンタ(2)
          size: 20
    
    block_on_stack();
    
        dump line:23
        byref data block 0x100150 contents:
          forwarding: 0x100150    ← stack上のBlockからheap上の__block変数を参照している
          flags: 0x1000002
          size: 20
    
    block_on_heap();
    
        dump line:23
        byref data block 0x100150 contents:
          forwarding: 0x100150    ← heap上のBlockからheap上の__block変数を使用している
          flags: 0x1000002
          size: 20
    
    printf("\n___ Block_release ___\n");
    Block_release(block_on_heap);
    dump(__LINE__, &total);
    
        dump line:40
        byref data block 0x100150 contents:
          forwarding: 0x100150
          flags: 0x1000001    ← Block_releaseしたので参照カウンタが減少(2→1)
          size: 20
    
    block_on_stack();
    
        dump line:23
        byref data block 0x100150 contents:
          forwarding: 0x100150    ← stack上のBlockからheap上の__block変数を使用。まだ参照カウンタが1なのでheap上に残存
          flags: 0x1000001
          size: 20
    
    return &total;
}
int main()
{
    dump(__LINE__, test());
    
        dump line:49
        byref data block 0x100150 contents:
          forwarding: 0x100150
          flags: 0x1000000    ← 参照カウンタが0なのでheap上に残ってないはず!
          size: 20
    
}
(Mac OS X 10.6、clang -m32で確認)
てことで、Block_copy使うと、本当にstackからheapに移動してることがわかりました。
ついでに、Block_releaseしても、heapからstackに戻すわけじゃなさそうなこともわかりました。
あ、dump関数が、「total.__forwarding->total」からのアクセスなので、Block_copy以降全部heapに存在しているように見えますが、stack上のtotal.__forwardingがちゃんとheap上のものをさしているので、stack上のtotal構造体を触っても平気です。
もはや誰得の情報なのか混迷を極めて来ましたが、つづきます。