#
9 循環参照ガベージコレクションをサポートする

Python が循環参照を含むガベージの検出とコレクションをサポートする には、他のオブジェクトに対する ``コンテナ'' (他のオブジェクトには 他のコンテナも含みます) となるオブジェクト型によるサポートが必要です。 他のオブジェクトに対する参照を記憶しないオブジェクトや、 (数値や文字列のような) アトム型 (atomic type) への参照だけを 記憶するような型では、ガベージコレクションに際して特別これといった サポートを提供する必要はありません。

ここで説明しているインタフェースの使い方を示した例は、 Python の拡張と埋め込み の ``循環参照の収集をサポートする'' にあります。 コンテナ型を作るには、型オブジェクトの `tp_flags` フィールド に`Py_TPFLAGS_HAVE_GC` フラグがなくてはならず、 `tp_traverse` ハンドラの実装を提供しなければなりません。 実装する型のインスタンスを変更可能なオブジェクトにするなら、 `tp_clear` の実装も提供しなければなりません。

`Py_TPFLAGS_HAVE_GC`
  このフラグをセットした型のオブジェクトは、この節に述べた規則に 適合しなければなりません。簡単のため、このフラグをセットした型の オブジェクトをコンテナオブジェクトと呼びます。

コンテナ型のコンストラクタは以下の二つの規則に適合しなければなりません:

1. オブジェクトのメモリは `PyObject_GC_New()` または `PyObject_GC_VarNew()` で確保しなければなりません。

2. 一度他のコンテナへの参照が入るかもしれないフィールドが全て 初期化されたら、`PyObject_GC_Track()` を呼び出さねば なりません。

TYPE* `PyObject_GC_New`( | TYPE, PyTypeObject *type)

  `PyObject_New()` に似ていますが、 `Py_TPFLAGS_HAVE_GC` のセットされたコンテナオブジェクト 用です。

TYPE* `PyObject_GC_NewVar`( | TYPE, PyTypeObject *type, Py_ssize_t size)

  `PyObject_NewVar()` に似ていますが、 `Py_TPFLAGS_HAVE_GC` のセットされたコンテナオブジェクト 用です。

PyVarObject * `PyObject_GC_Resize`( | PyVarObject *op, Py_ssize_t)

  `PyObject_NewVar()` が確保したオブジェクトのメモリを リサイズします。リサイズされたオブジェクトを返します。 失敗すると `NULL` を返します。

void `PyObject_GC_Track`( | PyObject *op)

  ガベージコレクタが追跡しているコンテナオブジェクトの集合に オブジェクト op を追加します。ガベージコレクタの動作する 回数は予測不能なので、追加対象にするオブジェクトは追跡されている 間ずっと有効なオブジェクトでなければなりません。 この関数は、通常コンストラクタの最後付近で、`tp_traverse` ハンドラ以降の全てのフィールドが有効な値になった時点で呼び出さねば なりません。

void `_PyObject_GC_TRACK`( | PyObject *op)

  `PyObject_GC_Track()` のマクロ版です。拡張モジュールに 使ってはなりません。

同様に、オブジェクトのメモリ解放関数も以下の二つの規則に適合しなければ なりません:

1. 他のコンテナを参照しているフィールドを無効化する前に、 `PyObject_GC_UnTrack()` を呼び出さねばなりません。
2. オブジェクトのメモリは `PyObject_GC_Del()` で解放しなければなりません。

void `PyObject_GC_Del`( | void *op)

  `PyObject_GC_New()` や `PyObject_GC_NewVar()` を使って確保されたメモリを解放します。

void `PyObject_GC_UnTrack`( | void *op)

  ガベージコレクタが追跡しているコンテナオブジェクトの集合から オブジェクト op を除去します。`PyObject_GC_Track()` を呼び出して、除去したオブジェクトを再度追跡対象セットに追加 できるので注意してください。メモリ解放関数 (deallocator, `tp_dealloc` ハンドラ) は、`tp_traverse` ハンドラが 使用しているフィールドのいずれかが無効化されるよりも 以前にオブジェクトに対して呼び出されていなければなりません。

void `_PyObject_GC_UNTRACK`( | PyObject *op)

  `PyObject_GC_UnTrack()` のマクロ版です。拡張モジュールに 使ってはなりません。

`tp_traverse` ハンドラは以下の型を持つ関数を引数の一つとして とります:

`int (*visitproc)(PyObject *object, void *arg)`
  `tp_traverse` ハンドラに渡すビジタ関数 (visitor function) の型です。この関数は追跡すべきオブジェクトを object に、 `tp_traverse` ハンドラの第三引数を arg にして呼び出され ます。Python のコア部分では、ガベージコレクションの実装に複数の ビジタ関数を使っています。ユーザが独自にビジタ関数を書く必要がある とは想定されていません。

`tp_traverse` ハンドラは以下の型でなければなりません:

`int (*traverseproc)(PyObject *self, visitproc visit, void *arg)`
  コンテナオブジェクトのためのトラバーサル関数 (traversal function) です。 実装では、self に直接入っている各オブジェクトに対してvisit 関数を呼び出さねばなりません。このとき、visit へのパラメタは コンテナに入っている各オブジェクトと、このハンドラに渡された arg の値です。visit 関数は `NULL` オブジェクトを引数に 渡して呼び出してはなりません。visit が非ゼロの値を返す場合、 エラーが発生し、戻り値をそのまま返すようににしなければなりません。

`tp_traverse` ハンドラの作成を単純化するため、`Py_VISIT()` マクロが提供されています。このマクロを使うには、`tp_traverse` の 実装で、引数を visit および arg という名前にしておかねば なりません:

void `Py_VISIT`( | PyObject *o)

  引数 o および arg を使ってvisit コールバックを呼び出し ます。visit が非ゼロの値を返した場合、その値をそのまま返します。 このマクロを使えば、`tp_traverse` ハンドラは以下のようになります:

      static int
      my_traverse(Noddy *self, visitproc visit, void *arg)
      {
          Py_VISIT(self->foo);
          Py_VISIT(self->bar);
          return 0;
      }

  バージョン 2.4 で 新たに追加 された仕様です。

`tp_clear` ハンドラは `inquiry` 型にするか、 オブジェクトが変更不能の場合には `NULL` にしなければなりません。 `NULL` if the object is immutable.

`int (*inquiry)(PyObject *self)`
  循環参照を形成しているとおぼしき参照群を放棄します。 変更不可能なオブジェクトは循環参照を直接形成することが決してない ので、この関数を定義する必要はありません。 このメソッドを呼び出した後でもオブジェクトは有効なままでなければ ならないので注意してください (参照に対して `Py_DECREF()` を呼ぶだけにしないでください)。ガベージコレクタは、オブジェクトが 循環参照を形成していることを検出した際にこのメソッドを呼び出します。