2011-01-27

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

いよいよ本日、2011年1月27日発売!の『iOS 4プログラミングブック』。

その1その2と「__block修飾子」について深追いしてきましたが、まだ潜ってみます。ぶくぶくぶく。


潜る前に、まずは地図を見ておきましょう。

179ページにあるとおり、Blocksのことが知りたかったらLLVMのドキュメントを参照します。


http://clang.llvm.org/docs/BlockLanguageSpec.txt
http://clang.llvm.org/docs/Block-ABI-Apple.txt


... しかしまぁ、言うは易く行うは難し。英語読むくらいならソースコード読むほうが楽ですね。地図なしで。


てことで、ソースを読んでみます。ぶくぶくぶく。

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修飾子については語りつくした感がありますが、まだつづくよ