# 使对象类型支持循环垃圾回收

Python 对循环引用的垃圾检测与回收需要“容器”对象类型的支持，此类型的容器对象中可能包含其它容器对象。不保存其它对象的引用的类型，或者只保存原子类型（如数字或字符串）的引用的类型，不需要显式提供垃圾回收的支持。

若要创建一个容器类，类型对象的 `tp_flags` 字段必须包含 `Py_TPFLAGS_HAVE_GC` 并提供一个 `tp_traverse` 处理的实现。如果该类型的实例是可变的，还需要实现 `tp_clear` 。

`Py_TPFLAGS_HAVE_GC`

  设置了此标志位的类型的对象必须符合此处记录的规则。为方便起见，下文把这些对象称为容器对象。

容器类型的构造函数必须符合两个规则：

1. 必须使用 `PyObject_GC_New()` 或 `PyObject_GC_NewVar()` 为这些对象分配内存。

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` 标签的容器对象。

TYPE* `PyObject_GC_Resize`(TYPE, PyVarObject *op, Py_ssize_t newsize)

  Resize an object allocated by `PyObject_NewVar()`. Returns the resized object or NULL on failure. op must not be tracked by the collector yet.

void `PyObject_GC_Track`( PyObject *op)

  把对象 op 加入到垃圾回收器跟踪的容器对象中。对象在被回收器跟踪时必须保持有效的，因为回收器可能在任何时候开始运行。在 `tp_traverse` 处理前的所有字段变为有效后，必须调用此函数，通常在靠近构造函数末尾的位置。

void `_PyObject_GC_TRACK`( PyObject *op)

  `PyObject_GC_Track()` 的宏实现版本。它不能被用于扩展模块。

  3.6 版後已棄用: 这个宏在 Python 3.8 中被移除。

同样的，对象的释放器必须符合两个类似的规则：

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()` 以将其加回到被跟踪对象集合。 释放器 (`tp_dealloc` 句柄) 应当在 `tp_traverse` 句柄所使用的任何字段失效之前为对象调用此函数。

void `_PyObject_GC_UNTRACK`( PyObject *op)

  `PyObject_GC_UnTrack()` 的使用宏实现的版本。不能用于扩展模块。

  3.6 版後已棄用: 这个宏在 Python 3.8 中被移除。

`tp_traverse` 处理接收以下类型的函数形参。

int `(*visitproc)`( PyObject *object, void *arg)

  传给 `tp_traverse` 处理的访问函数的类型。object 是容器中需要被遍历的一个对象，第三个形参对应于 `tp_traverse` 处理的 arg 。Python核心使用多个访问者函数实现循环引用的垃圾检测，不需要用户自行实现访问者函数。

`tp_traverse` 处理必须是以下类型：

int `(*traverseproc)`( PyObject *self, visitproc visit, void *arg)

  Traversal function for a container object. Implementations must call the visit function for each object directly contained by self, with the parameters to visit being the contained object and the arg value passed to the handler. The visit function must not be called with a NULL object argument. If visit returns a non-zero value that value should be returned immediately.

为了简化 `tp_traverse` 处理的实现，Python提供了一个 `Py_VISIT()` 宏。若要使用这个宏，必须把 `tp_traverse` 的参数命名为 visit 和 arg 。

void `Py_VISIT`( PyObject *o)

  If o is not NULL, call the visit callback, with arguments o and arg. If visit returns a non-zero value, then return it. Using this macro, `tp_traverse` handlers look like:

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

The `tp_clear` handler must be of the `inquiry` type, or NULL if the object is immutable.

int `(*inquiry)`( PyObject *self)

  丢弃产生循环引用的引用。不可变对象不需要声明此方法，因为他们不可能直接产生循环引用。需要注意的是，对象在调用此方法后必须仍是有效的（不能对引用只调用 `Py_DECREF()` 方法）。当垃圾回收器检测到该对象在循环引用中时，此方法会被调用。