2011-01-26

『iOS 4プログラミングブック』 第5章マルチスレッド 補遺 その2

『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にあるか、気にせずにアクセスできるわけです。


確かめてみましょう!

  1. #import <Foundation/Foundation.h>  
  2. #import <stdio.h>  
  3.   
  4. extern const char *_Block_byref_dump(void *);  
  5.   
  6. void dump(int line, int *p)  
  7. {  
  8.     p -= 4;  
  9.     printf("\ndump line:%d\n", line);  
  10.     puts(_Block_byref_dump(p));  
  11. }  
  12.   
  13. int *test()  
  14. {  
  15.     __block int total = 11;  
  16.   
  17.     dump(__LINE__, &total);  
  18.   
  19.     void (^block_on_stack)() = ^{  
  20.   
  21.         ++total;  
  22.   
  23.         dump(__LINE__, &total);  
  24.     };  
  25.   
  26.     block_on_stack();  
  27.   
  28.     printf("\n___ Block_copy ___\n");  
  29.     void (^block_on_heap)() = Block_copy(block_on_stack);  
  30.   
  31.     dump(__LINE__, &total);  
  32.   
  33.     block_on_stack();  
  34.   
  35.     block_on_heap();  
  36.   
  37.     printf("\n___ Block_release ___\n");  
  38.     Block_release(block_on_heap);  
  39.   
  40.     dump(__LINE__, &total);  
  41.   
  42.     block_on_stack();  
  43.   
  44.     return &total;  
  45. }  
  46.   
  47. int main()  
  48. {  
  49.     dump(__LINE__, test());  
  50. }  


__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構造体を触っても平気です。

もはや誰得の情報なのか混迷を極めて来ましたが、つづきます。