Objective-CエンジニアのためのObjective-C Blocks入門
Mac OS X v10.6以降とiOS 4.0以降ではObjective-CにBlocksというシンタックスが追加されているらしい。
- Blocks Programming Topics: Introduction
- http://unknownplace.org/memo/2010/05/11/1/
- RubyエンジニアのためのObjective-C Blocks入門 : As Sloth As Possible
後者のエントリから引用:
void (^f)(id) = ^(id x) { NSLog(@"世界の全てを敵に回しても、僕は%@の味方だ", x); }; f(@"うどん");
id型(Object)としてコレクションに突っ込んだりも出来るし、関数呼び出しのように使うこともできるらしい。つまり…どういうことなんだってばよ?
てことで調べてみた。
まとめ
- Blocksは__NSGlobalBlock__、__NSStackBlock__、__NSAutoBlock__、__NSMallocBlock__などのクラスのインスタンスとして振る舞う。
- Blocks関係のクラスは、CoreFoundation.frameworkで定義されている。
- 関数スコープを参照している場合__NSStackBlock__が、それ以外の場合__NSGlobalBlock__が使われる。
- __NSStackBlock__であるBlocksをコピーすると、GCありの場合__NSAutoBlock__、なしの場合__NSMallocBlock__として複製される。
- Blocksは実際には__block_literal_1、__block_literal_2などの構造体として定義される。
- これらの構造体は、先頭にisaを持つ、いわゆるToll-free bridgeで実現されている。
- Toll-free bridgeについてはhttp://journal.mycom.co.jp/column/objc/038/index.htmlなど参照。
- 外部の変数を参照する場合、同じ名前の構造体のメンバーとして定義され、作成時に値がコピーされる。
- 変数に__blockを付けた場合、__Block_byref_XXXという構造体が定義され、それのポインタの形で保持される。
- __Block_byref_XXXの実体は同じ変数を使うBlocks間で共有される。
- 変数への参照がある場合、Blocksの数だけコピー用関数、解放用関数が定義され、descriptorにその参照が保持される。
- これらの構造体は、先頭にisaを持つ、いわゆるToll-free bridgeで実現されている。
- Blocksの実際の処理は、関数として定義される
- Blocksについては、LLVMのソースを見ればいろいろ分かる
- http://llvm.org/svn/llvm-project/compiler-rt/trunk
- _Block_dump()という関数でBlocksの内容を文字列で取得し、表示できる。
目次
- Blocksのクラス構成を調べる
- Blocksのメモリレイアウトを調べる
- 構造体 __block_literal_Xの内容を調べる
- __FuncPtrの内容を調べる
- なんちゃってBlocksを作ってみる
- ソースコードを見てみる
Blocksのクラス構成を調べる
Objectってことはクラスがあるはずなので、実際に動かして調べてみた。
プログラム
int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int (^inc)(int) = ^(int x) { return x + 1; }; NSLog(@"result = %d", inc(6)); NSLog(@"class of inc: %@", [inc class]); [pool drain]; return 0; }
出力
2010-10-31 23:54:51.374 BlocksSample[34421:a0f] result = 7 2010-10-31 23:54:51.377 BlocksSample[34421:a0f] class of inc: __NSGlobalBlock__
クラス名は「__NSGlobalBlock__」という名前らしい。とりあえずリファレンスには無いクラス。
superclassを手繰ってクラス階層を見てみる。
Class aClass = [inc class]; while (aClass) { NSLog(@"%@ : %p", aClass, aClass); aClass = [aClass superclass]; }
2010-11-03 18:38:21.336 BlocksSample[44352:a0f] __NSGlobalBlock__ : 0x7fff70a72700 2010-11-03 18:38:21.337 BlocksSample[44352:a0f] __NSGlobalBlock : 0x7fff702079a8 2010-11-03 18:38:21.338 BlocksSample[44352:a0f] NSBlock : 0x7fff70207b38 2010-11-03 18:38:21.338 BlocksSample[44352:a0f] NSObject : 0x7fff702074a8
NSObject > NSBlock > __NSGlobalBlock > __NSGlobalBlock__という階層になっているようだ。
NSBlock関係のクラスはCoreFoundation.frameworkで定義されていて、以下のような階層になっている。
- NSObject
- NSBlock
- __NSGlobalBlock
- __NSGlobalBlock__
- __NSAutoBlock
- __NSAutoBlock__
- __NSFinalizingBlock
- __NSFinalizingBlock__
- __NSMallocBlock
- __NSMallocBlock__
- __NSStackBlock
- __NSStackBlock__
- __NSGlobalBlock
- NSBlock
スタック上の変数への参照がないと__NSGlobalBlock__に、あると__NSStackBlock__になり、__NSStackBlock__のBlocksをコピーすると__NSMallocBlock__または__NSAutoBlock__になるようだ。__NSMallocBlock__が使われるか__NSAutoBlock__が使われるかは、GCが有効かどうかによるようだ。
参照ありサンプル:
NSString *greetingFormat=@"Hello, %@!"; // generateGreetingMessage id (^mess)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; }; NSLog(@"message = '%@'", mess(@"World")); NSLog(@"class of mess: %@ : %p", [mess class], [mess class]); int (^cloneOfMess)(int) = [[mess copy] autorelease]; NSLog(@"class of cloneOfMess: %@ : %p", [cloneOfMess class], [cloneOfMess class]);
出力(GCなし)
2010-11-03 20:19:12.407 BlocksSample[45178:a0f] message = 'Hello, World!' 2010-11-03 20:19:12.407 BlocksSample[45178:a0f] class of mess: __NSStackBlock__ : 0x7fff70a72300 2010-11-03 20:19:12.408 BlocksSample[45178:a0f] class of cloneOfMess: __NSMallocBlock__ : 0x7fff70a72400
2010-11-03 20:23:02.463 BlocksSample[45244:a0f] message = 'Hello, World!' 2010-11-03 20:23:02.466 BlocksSample[45244:a0f] class of mess: __NSStackBlock__ : 0x7fff70a72300 2010-11-03 20:23:02.466 BlocksSample[45244:a0f] class of cloneOfMess: __NSAutoBlock__ : 0x7fff70a72500
Blocksのメモリレイアウトを調べる
Blockが__NSXXBlocks__シリーズのインスタンスなのは分かったが、ではスタックへの参照をどうやって保持しているのか。メモリレイアウトを調べてみる。
オブジェクトで値を保持すると言えばインスタンス変数。各クラスのインスタンス変数を見てみる。
void printIvars(Class aClass) { unsigned int ivarCount = 0; Ivar *ivars = class_copyIvarList(aClass, &ivarCount); NSLog(@"ivars of %@: %d", aClass, ivarCount); for (int i = 0; i < ivarCount; i++) { NSLog(@"\t%s (offset: %d, type: %s)", ivar_getName(ivars[i]), ivar_getOffset(ivars[i]), ivar_getTypeEncoding(ivars[i])); } free(ivars); }
__NSStackBlock__、 __NSAutoBlock__、__NSMallocBlock__の各クラスとその親クラスのインスタンス変数の数を表示した結果(重複は除く)
2010-11-03 20:42:39.086 BlocksSample[45608:a0f] ivars of __NSStackBlock__: 0 2010-11-03 20:42:39.087 BlocksSample[45608:a0f] ivars of __NSStackBlock: 0 2010-11-03 20:42:39.087 BlocksSample[45608:a0f] ivars of NSBlock: 0 2010-11-03 20:42:39.088 BlocksSample[45608:a0f] ivars of NSObject: 1 2010-11-03 20:42:39.088 BlocksSample[45608:a0f] isa (offset: 0, type: #) ... 2010-11-03 20:43:31.898 BlocksSample[45664:a0f] ivars of __NSAutoBlock__: 0 2010-11-03 20:43:31.899 BlocksSample[45664:a0f] ivars of __NSAutoBlock: 0 ... 2010-11-03 20:44:32.303 BlocksSample[45714:a0f] ivars of __NSMallocBlock__: 0 2010-11-03 20:44:32.304 BlocksSample[45714:a0f] ivars of __NSMallocBlock: 0 ...
すべてのクラスを通じて、インスタンス変数はNSObjectのisaしかない。どういうことか。これはObjective-C環境とC言語環境で同じライブラリを共有する時によく使うテクニックで、Toll-free bridge*2と呼ばれるもののようだ。
クラスのインスタンスサイズと同じサイズのメモリを確保し、領域の先頭(isa)にクラスへのポインタを格納しておけば、Objective-Cの文脈ではオブジェクトとして振る舞わせることが出来る。以前はobjc_class構造体のinstance_sizeを大きめに取っていたような気がするけど、今はインスタンス毎のextraBytesという概念があるようで、object_getIndexedIvars()で取得したりするようだ。
NSLog(@"instance size of %@ is: %d", [NSObject class], class_getInstanceSize([NSObject class])); NSLog(@"instance size of %@ is: %d", [mess class], class_getInstanceSize([mess class])); NSLog(@"block = %p", mess); void *extraBytes = object_getIndexedIvars(mess); NSLog(@"extraBytes = %p", extraBytes);
2010-11-03 21:24:28.869 BlocksSample[46185:a0f] instance size of NSObject is: 8 2010-11-03 21:24:28.869 BlocksSample[46185:a0f] instance size of __NSStackBlock__ is: 8 2010-11-03 21:24:28.870 BlocksSample[46185:a0f] block = 0x7fff5fbff6e0 2010-11-03 21:24:28.870 BlocksSample[46185:a0f] extraBytes = 0x7fff5fbff6e8
ポインタを見る限り、インスタンスと連続した領域に確保されている。
デバッガで見てみる。
2010-11-03 21:43:02.668 BlocksSample[46293:a0f] instance size of NSObject is: 8 232 NSLog(@"instance size of %@ is: %d", [mess class], class_getInstanceSize([mess class])); (gdb) 2010-11-03 21:43:03.456 BlocksSample[46293:a0f] instance size of __NSStackBlock__ is: 8 233 NSLog(@"block = %p", mess); (gdb) 2010-11-03 21:43:04.392 BlocksSample[46293:a0f] block = 0x7fff5fbff880 234 void *extraBytes = object_getIndexedIvars(mess); (gdb) 235 NSLog(@"extraBytes = %p", extraBytes); (gdb) p mess $5 = (struct __block_literal_2 *) 0x7fff5fbff880 (gdb) p extraBytes $6 = (void *) 0x7fff5fbff888
Blocks 「mess」の型が、struct __block_literal_2と表示される。厳密には通常の__NSStackBlock__というクラスのインスタンスではなく、struct __block_literal_2という構造体だったようだ。
構造体の定義を表示してみる。
(gdb) ptype struct __block_literal_2 type = struct __block_literal_2 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; NSString *greetingFormat; }
先頭が__isaになっていて、__NSStackBlock__クラスへのポイントが格納されているようだ。
(gdb) p mess->__isa $3 = (void *) 0x7fff70a72300 (gdb) po mess->__isa __NSStackBlock__
また、Blocks "mess"が内部で参照している関数スコープのローカル変数greetingFormatが、構造体のメンバーとして定義されており、内容がコピーされている。
(gdb) p mess $6 = (struct __block_literal_2 *) 0x7fff5fbff880 (gdb) po mess->greetingFormat Hello, %@!
構造体 __block_literal_Xの内容を調べる
Blocksを定義した順に__block_literal_1、__block_literal_2、...と順に構造体が定義されるようだ。
int (^inc)(int) = ^(int x) { return x + 1; }; NSString *greetingFormat=@"Hello, %@!"; id (^mess)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; }; int (^cloneOfMess)(int) = [[mess copy] autorelease];
(gdb) p inc $1 = (struct __block_literal_1 *) 0x100002380 (gdb) ptype struct __block_literal_1 type = struct __block_literal_1 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor *__descriptor; } (gdb) p mess $2 = (struct __block_literal_2 *) 0x7fff5fbff6c0 (gdb) ptype struct __block_literal_2 type = struct __block_literal_2 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; NSString *greetingFormat; } (gdb) p cloneOfMess $3 = (struct __block_literal_2 *) 0x100110ad0
__NSGlobalBlock__と__NSStackBlock__用に使われる構造体は参照値コピー用の追加部分(greetingFormat)が違う以外に、__descriptorの型も違うようだ。__NSStackBlock__とそれをコピーした__NSMallocBlock__は同じ構造体を使うようである。
GC有効(-fobjc-gc-only)だとコピーした時に型が変わる。つまり__NSAutoBlock__系のものには別の構造体が使われる。
(gdb) p cloneOfMess $6 = (struct __block_literal_2 *) 0x20000da60 (gdb) ptype struct __block_literal_generic type = struct __block_literal_generic { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor *__descriptor; }
参照している変数が__block付きだと、メンバーが__Block_byref_XXXに変わる。また、複数のBlocksから参照されている変数は参照先が同じになるようだ。
__block NSString *greetingFormat=@"Hello, %@!"; // generateGreetingMessage id (^mess)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; }; id (^mess2)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; };
(gdb) p mess $1 = (struct __block_literal_2 *) 0x7fff5fbff610 (gdb) ptype struct __block_literal_2 type = struct __block_literal_2 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; struct __Block_byref_1_greetingFormat *greetingFormat; } (gdb) p mess2 $2 = (struct __block_literal_3 *) 0x7fff5fbff5e0 (gdb) ptype struct __block_literal_3 type = struct __block_literal_3 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; struct __Block_byref_1_greetingFormat *greetingFormat; } (gdb) ptype struct __Block_byref_1_greetingFormat type = struct __Block_byref_1_greetingFormat { void *__isa; struct __Block_byref_1_greetingFormat *__forwarding; unsigned int __flags; unsigned int __size; void *__ByrefKeepFuncPtr; void *__ByrefDestroyFuncPtr; NSString *greetingFormat; } (gdb) p mess->greetingFormat $3 = (struct __Block_byref_1_greetingFormat *) 0x7fff5fbff5b0 (gdb) p mess2->greetingFormat $4 = (struct __Block_byref_1_greetingFormat *) 0x7fff5fbff5b0 (gdb) po mess->greetingFormat->greetingFormat Hello, %@!
参照が複数あった場合は、すべて構造体のメンバーとして追加される(なぜか逆順。)
int a1 = 1, a2 = 2, a3 = 3, a4 = 4, a5 = 5; int (^reftest)(void) = ^ { return a1 + a2 + a3 + a4 + a5; };
(gdb) p reftest $1 = (struct __block_literal_3 *) 0x7fff5fbff660 (gdb) ptype struct __block_literal_3 type = struct __block_literal_3 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor *__descriptor; int a5; int a4; int a3; int a2; int a1; }
__blockを付けると、個別に__Block_byref_XXXが作られるようだ。
__block int a1=1,a2=2,a3=3,a4=4,a5=5; int (^reftest)(void) = ^ { return a1 + a2 + a3 + a4 + a5; };
(gdb) p reftest $2 = (struct __block_literal_3 *) 0x7fff5fbff5c0 (gdb) ptype struct __block_literal_3 type = struct __block_literal_3 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; struct __Block_byref_5_a5 *a5; struct __Block_byref_4_a4 *a4; struct __Block_byref_3_a3 *a3; struct __Block_byref_2_a2 *a2; struct __Block_byref_1_a1 *a1; } (gdb) ptype struct __Block_byref_5_a5 type = struct __Block_byref_5_a5 { void *__isa; struct __Block_byref_5_a5 *__forwarding; unsigned int __flags; unsigned int __size; int a5; }
まとめると、外部のスコープの値を参照している場合、Blocksがその値を保持するために、それぞれのBlocksに対する構造体が定義されており、構造体のメンバーとして参照値が定義されている、ということになる。
確保した値を解放したり、別のBlocksにコピーする為に、コピー用の関数と破棄用の関数が作られ、__descriptorのメンバーにセットされるようだ。
(gdb) ptype struct __block_literal_2 type = struct __block_literal_2 { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; NSString *greetingFormat; } (gdb) ptype struct __block_descriptor_withcopydispose type = struct __block_descriptor_withcopydispose { long unsigned int reserved; long unsigned int Size; void *CopyFuncPtr; void *DestroyFuncPtr; } (gdb) p *mess->__descriptor $1 = { reserved = 0, Size = 40, CopyFuncPtr = 0x10000176c, DestroyFuncPtr = 0x100001746 } (gdb) p __copy_helper_block_2 $2 = {void (struct __block_literal_2 *, struct __block_literal_2 *)} 0x10000176c <__copy_helper_block_2> (gdb) p __destroy_helper_block_2 $3 = {void (struct __block_literal_2 *)} 0x100001746 <__destroy_helper_block_2>
__descriptorのCopyFuncPtrに__copy_helper_block_2という関数が、DestroyFuncPtrに__destroy_helper_block_2という関数のポインタがセットされている。関数の内容は残念ながら分からないが、構造体ごとに異なるメンバー分のコピーや破棄の処理が書いてあると思われる。
__FuncPtrの内容を調べる
値の保持方法は分かったが、では実際の処理はどのように呼び出されるのか。Blocks定義内の処理にブレークポイントが設定できるので、gdbで様子を見てみる。
NSString *greetingFormat=@"Hello, %@!"; id (^mess)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; // ←この行にブレークポイント設定 }; NSLog(@"message = '%@'", mess(@"World"));
(gdb) where #0 __main_block_invoke_2 (.block_descriptor=0x7fff5fbff620, name=0x100002128) at /Users/terazzo/Labs/SnowLeopard/BlocksSample/BlocksSample.m:209 #1 0x0000000100001290 in main (argc=1, argv=0x7fff5fbff778) at /Users/terazzo/Labs/SnowLeopard/BlocksSample/BlocksSample.m:221 (gdb) p __main_block_invoke_2 $1 = {id (struct __block_literal_2 *, id)} 0x1000017b9 <__main_block_invoke_2> (gdb) p *(struct __block_literal_2 *)0x7fff5fbff620 $2 = { __isa = 0x7fff70a72300, __flags = 570425344, __reserved = 0, __FuncPtr = 0x1000017b9, __descriptor = 0x1000023e0, greetingFormat = 0x7fff5fbff5f0 } (gdb) po 0x100002128 World
今回は__FuncPtrに設定される関数は「__main_block_invoke_2」という名前で定義されており(アドレスが0x1000017b9であることから特定できる)、引数は二個ある。第一引数はBlocks自体、第二引数以降は実際の引数のようだ。(これもオブジェクト指向言語とC言語のハイブリット環境でよく使うテクニック。)
なんちゃってBlocksを作ってみる
以上のことを踏まえて、Blocks定義した時にコンパイラがやっていることを手動でやってみて、ちゃんと呼び出しできるか見てみる。
struct __block_descriptor_withcopydispose { long unsigned int reserved; long unsigned int Size; void *CopyFuncPtr; void *DestroyFuncPtr; }; // Blocksを格納する構造体を定義 struct __block_literal_X { void *__isa; int __flags; int __reserved; void *__FuncPtr; struct __block_descriptor_withcopydispose *__descriptor; NSString *greetingFormat; }; // __FuncPtrにセットする関数、つまりBlocksの実際の処理をおこなう関数を定義 NSString * __main_block_invoke_X(struct __block_literal_X *self, NSString *name) { return [NSString stringWithFormat:self->greetingFormat, name]; } // コピー用の関数。今回はダミー。コピー先にgreetingFormatをセットしてretainすれば良いかな? void __copy_helper_block_X(struct __block_literal_X *block1, struct __block_literal_X *block2) { } // 解放用の関数。今回はダミー。greetingFormatをreleaseすれば良いかな? void __destroy_helper_block_X(struct __block_literal_X *block) { } int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSString *greetingFormat = @"Hello, %@!"; // block_descriptorを先に作っておく struct __block_descriptor_withcopydispose block_descriptor = { 0, // reserved sizeof(struct __block_literal_X), // Size __copy_helper_block_X, // CopyFuncPtr __destroy_helper_block_X // DestroyFuncPtr }; // Blocksを構造体として宣言 struct __block_literal_X _mess = { NSClassFromString(@"__NSStackBlock__"), // __isa 0, // __flags ※適当 0, // __reserved __main_block_invoke_X, // __FuncPtr &block_descriptor, // __descriptor greetingFormat // greetingFormat ※変数の値をコピー }; // Blocksにキャスト id (^mess)(id) = (void *) &_mess; // 呼び出し NSLog(@"message = '%@'", mess(@"World")); // オブジェクトとして表示してみる NSLog(@"description = %@", mess); [pool drain]; return 0; }
実行結果
2010-11-04 01:36:41.814 BlocksSample[48701:a0f] message = 'Hello, World!' 2010-11-04 01:36:41.817 BlocksSample[48701:a0f] description = <__NSStackBlock__: 0x7fff5fbff6e0>
無事に実行できた。
今回作成したのはなんちゃってBlocksであって、コピー等が出来ない出来損ないなので注意。
ソースコードを見てみる
Blocks関係のソースコードがLLVMのプロジェクトで見れるらしい。
SVNで公開されているのでexportしてみることもできる。
_Block_dump()という関数があったので、いろいろ表示してみた。
extern const char *_Block_dump(const void *block); int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int (^inc)(int) = ^(int x) { return x + 1; }; NSString *greetingFormat=@"Hello, %@!"; // generateGreetingMessage id (^mess)(id) = ^(id name) { return [NSString stringWithFormat:greetingFormat, name]; }; int (^cloneOfMess)(int) = [[mess copy] autorelease]; __block int a1=1,a2=2,a3=3,a4=4,a5=5; int (^reftest)(void) = ^ { return a1 + a2 + a3 + a4 + a5; }; NSLog(@"inc: %s", _Block_dump(inc)); NSLog(@"mess: %s", _Block_dump(mess)); NSLog(@"cloneOfMess: %s", _Block_dump(cloneOfMess)); NSLog(@"reftest: %s", _Block_dump(reftest)); [pool drain]; return 0; }
GCなし
2010-11-04 02:14:21.127 BlocksSample[48981:a0f] inc: ^0x1000022a0 (new layout) = isa: global Block flags: HASDESCRIPTOR refcount: 0 invoke: 0x10000193a descriptor: 0x1000022e0 descriptor->reserved: 0 descriptor->size: 32 2010-11-04 02:14:21.130 BlocksSample[48981:a0f] mess: ^0x7fff5fbff640 (new layout) = isa: stack Block flags: HASDESCRIPTOR HASHELP refcount: 0 invoke: 0x1000018f3 descriptor: 0x100002300 descriptor->reserved: 0 descriptor->size: 40 descriptor->copy helper: 0x1000018c1 descriptor->dispose helper: 0x10000189b 2010-11-04 02:14:21.130 BlocksSample[48981:a0f] cloneOfMess: ^0x10010c710 (new layout) = isa: malloc heap Block flags: HASDESCRIPTOR FREEME HASHELP refcount: 1 invoke: 0x1000018f3 descriptor: 0x100002300 descriptor->reserved: 0 descriptor->size: 40 descriptor->copy helper: 0x1000018c1 descriptor->dispose helper: 0x10000189b 2010-11-04 02:14:21.131 BlocksSample[48981:a0f] reftest: ^0x7fff5fbff5f0 (new layout) = isa: stack Block flags: HASDESCRIPTOR HASHELP refcount: 0 invoke: 0x10000180f descriptor: 0x100002320 descriptor->reserved: 0 descriptor->size: 72 descriptor->copy helper: 0x100001775 descriptor->dispose helper: 0x100001707
GCあり
2010-11-04 02:17:03.610 BlocksSample[49021:a0f] inc: ^0x1000022a0 (new layout) = isa: global Block flags: HASDESCRIPTOR refcount: 0 invoke: 0x100001937 descriptor: 0x1000022e0 descriptor->reserved: 0 descriptor->size: 32 2010-11-04 02:17:03.615 BlocksSample[49021:a0f] mess: ^0x7fff5fbff650 (new layout) = isa: stack Block flags: HASDESCRIPTOR HASHELP refcount: 0 invoke: 0x1000018f0 descriptor: 0x100002300 descriptor->reserved: 0 descriptor->size: 40 descriptor->copy helper: 0x1000018be descriptor->dispose helper: 0x100001898 2010-11-04 02:17:03.616 BlocksSample[49021:a0f] cloneOfMess: ^0x20000eee0 (new layout) = isa: GC heap Block flags: HASDESCRIPTOR ISGC HASHELP refcount: 0 invoke: 0x1000018f0 descriptor: 0x100002300 descriptor->reserved: 0 descriptor->size: 40 descriptor->copy helper: 0x1000018be descriptor->dispose helper: 0x100001898 2010-11-04 02:17:03.616 BlocksSample[49021:a0f] reftest: ^0x7fff5fbff600 (new layout) = isa: stack Block flags: HASDESCRIPTOR HASHELP refcount: 0 invoke: 0x10000180c descriptor: 0x100002320 descriptor->reserved: 0 descriptor->size: 72 descriptor->copy helper: 0x100001772 descriptor->dispose helper: 0x100001704
コピーなどの詳細はここのソースコードにあるが、最初に生成する部分については、コンパイラのソースコードを見ないと出てこないようだ。
*1:設定方法は、プロジェクト設定を編集>ビルド>GCC 4.2 - コード生成>Objective-Cガベージコレクションの値を 必須[-fobjc-gc-only]にする
*2:http://journal.mycom.co.jp/column/objc/038/index.htmlなど参照