Objective-C(2.0)において、オブジェクトは必ずしもヒープ上にないらしいので、少し調べた。
ブロックオブジェクト
ブロックはヒープ上にあるときとないときがある。
ブロックを作ると、その情報はブロックオブジェクトが保持する。
ブロックオブジェクトのクラスには、
- 何もキャプチャしない
__NSGlobalBlock__
(_NSConcreteGlobalBlock
) - 情報をスタック上に持つ
__NSStackBlock__
(_NSConcreteStackBlock
) - 情報をヒープに持つ
__NSMallocBlock__
(_NSConcreteMallocBlock
)
の3種類がある。ブロックオブジェクトもObjective-Cのオブジェクトであり、Block_private.hにレイアウト Block_layout
の定義がある。
__NSGlobalBlock__
キャプチャする情報がないブロックは、プログラムの一部としてコンパイルされ、動的なメモリを消費しない。このときブロックオブジェクトはオブジェクトではあるが(メッセージを受け取ることはできる)、実体は一つである。
globalスコープに置いた場合だけでなく、ブロックを関数内で宣言しても、キャプチャする情報がなければこの型になる。
__NSStackBlock__
キャプチャする情報があるとき、ブロックオブジェクトは __NSStackBlock__
として作られるようである。
その場でブロックを定義して呼び出すだけのときは、ブロックオブジェクトのライフサイクルは関数のスコープで終了し、解放される。
コールバック関数のようなケースでは、ブロックをretainしておく必要がある(普通はブロックを受け取る側が行う。ARC環境下では強参照で保持)。 __NSStackBlock__
クラスのオブジェクトをretainしようとすると、 __NSMallocBlock__
クラスに変換(コピー)されてからretainされるようになっている。
__NSMallocBlock__
ヒープ上に置かれたブロックオブジェクトである。
retainされることがわかっているなら、呼び出す時に最初から __NSMallocBlock__
で作る手段があってもよいような気がするが、試した限りでは常に __NSStackBlock__
として作られるようである。
tagged pointer
id
に中身も入っている場合がある。このときオブジェクトの実体は id
なので、 id
がスタックにあればオブジェクトはスタック上にあることになる。