その1、その2と「__block修飾子」について深追いしてきましたが、まだ潜ってみます。ぶくぶくぶく。
潜る前に、まずは地図を見ておきましょう。
179ページにあるとおり、Blocksのことが知りたかったらLLVMのドキュメントを参照します。
... しかしまぁ、言うは易く行うは難し。英語読むくらいならソースコード読むほうが楽ですね。地図なしで。
てことで、ソースを読んでみます。ぶくぶくぶく。
Block_copyとかBlock_releaseとかは、C言語のランタイムライブラリの一部としてLLVMのソースツリーに入ってます。
https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/Block_private.h
https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/runtime.c
このへん。
その2で出てきた
flags: 0x1000002
のbit定義がありますね。
enum {
BLOCK_REFCOUNT_MASK = (0xffff),
BLOCK_NEEDS_FREE = (1 << 24),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
BLOCK_IS_GC = (1 << 27),
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_DESCRIPTOR = (1 << 29)
};
BLOCK_REFCOUNT_MASK = 0xffff... 65536回Block_copyするとやばそうですね!
それはさておきBlock_copyすると、__block変数は_Block_byref_assign_copy()でcopyされます。
/*
* Runtime entry points for maintaining the sharing knowledge of byref data blocks.
*
* A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
* Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
* We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment it.
* Otherwise we need to copy it and update the stack forwarding pointer
* XXX We need to account for weak/nonretained read-write barriers.
*/
static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
forwarding
ポインタに代入しているところを見ると
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy
heap上構造体のforwardingも、stack上構造体のforwardingも、heap上の構造体を示すように書き換えられてます。安心して__block変数を操作できますね。
その2で使った秘密の_Block_byref_dump()もありますよ。
ちなみに、Mac OS X 10.5/iOS 3.1.3以前でもBlocksを使えるようにするplblocksてのがあります。
http://code.google.com/p/plblocks/source/browse/Runtime/trunk/Source/Runtime/Block.m
たとえばiOS SDK 4.2に入っているtoolchain(gcc, llvm-gcc, clang)は、Block構文をコンパイルすることが可能です。ということで実は、Deployment Targetを3.1.3にしても(Deployment Targetについては、iOS 4プログラミングブック 第10章ユニバーサル対応で詳しく説明してますよ!)、stack上でだけならBlocksを使うことができます。ただしBlocks用ランタイムがないため、Block_copy/Block_releaseなどはできません。そこで、plblocks runtimeのヘッダをインクルードして、plblocks runtimeのソースとともにコンパイルすると、plblocksのBlock_copy/Block_release実装を使えるようになるわけです。
ついでにlibdispatchを使えば、iOS3.1.3とかでもGrand Central Dispatchまで使えるわけですが、それはまた別の講釈で...
__block修飾子については語りつくした感がありますが、まだつづくよ