Objective-CエンジニアのためのObjective-C Blocks入門

Mac OS X v10.6以降とiOS 4.0以降ではObjective-CにBlocksというシンタックスが追加されているらしい。

後者のエントリから引用:

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で実現されている。
    • 外部の変数を参照する場合、同じ名前の構造体のメンバーとして定義され、作成時に値がコピーされる。
      • 変数に__blockを付けた場合、__Block_byref_XXXという構造体が定義され、それのポインタの形で保持される。
      • __Block_byref_XXXの実体は同じ変数を使うBlocks間で共有される。
    • 変数への参照がある場合、Blocksの数だけコピー用関数、解放用関数が定義され、descriptorにその参照が保持される。
  • Blocksの実際の処理は、関数として定義される
    • __main_block_invoke_1、__main_block_invoke_2などのような名前でBlocksの数だけ定義される。
    • この関数の第一引数はBlocks自身で、第二引数以降が実際の引数である。
    • 関数へのポインタが、__FuncPtrに保持される。
  • Blocksについては、LLVMのソースを見ればいろいろ分かる

目次

  1. Blocksのクラス構成を調べる
  2. Blocksのメモリレイアウトを調べる
  3. 構造体 __block_literal_Xの内容を調べる
  4. __FuncPtrの内容を調べる
  5. なんちゃってBlocksを作ってみる
  6. ソースコードを見てみる

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__に、あると__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

出力(GC必須)*1

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など参照