Objective-C

Objective-Cでヒープ上にないオブジェクトについて

More than 1 year has passed since last update.

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 がスタックにあればオブジェクトはスタック上にあることになる。

参考情報