浮动引用

浮动引用

注意:浮动引用是 C 便捷的 API,不应在现代 GObject 代码中使用。特别是语言绑定会认为该概念存在严重问题,因为浮动引用无法通过注释识别,并且也不符合浮动引用行为的偏差,比如说那些继承自 GInitiallyUnowned 并且仍然从 g_object_new() 返回完全引用的类型。

GInitiallyUnowned 是从 GObject 派生的。两者之间的唯一区别是,GInitiallyUnowned 的初始引用被标记为“浮动”引用。这意味着它并不专门声称由任何代码部分“拥有”。提供浮动引用的主要动机是 C 的便利性。特别是,它允许将代码写成 as

container = create_container ();
container_add_child (container, create_child());

如果 container_add_child() 在传入的子级上调用 g_object_ref_sink(),那么新创建的子级的引用就不会泄漏。如果没有浮动引用,container_add_child() 只能获取新的子级引用,因此要实现此代码而不会出现引用泄漏,则必须将其写成 as

Child *child;
container = create_container ();
child = create_child ();
container_add_child (container, child);
g_object_unref (child);

可以通过调用 g_object_ref_sink() 将浮动引用转换为普通引用。对于已经下沉的对象(不再具有浮动引用的对象),g_object_ref_sink() 等同于 g_object_ref(),并返回新的 引用。

由于浮动引用几乎仅对 C 方便有用,因此在 API 中提供自动引用和内存所有权维护(例如智能指针或垃圾回收)的语言绑定不应在它们的 API 中公开浮动引用。处理最初具有浮动引用的类型的最佳做法是在 g_object_new() 返回后立即下沉这些引用,方法是检查 GType 是否从 GInitiallyUnowned 继承。例如,

GObject *res = g_object_new_with_properties (gtype,
                                             n_props,
                                             prop_names,
                                             prop_values);

// or: if (g_type_is_a (gtype, G_TYPE_INITIALLY_UNOWNED))
if (G_IS_INITIALLY_UNOWNED (res))
  g_object_ref_sink (res);

return res;

某些对象实现可能需要跨特定代码部分保存对象的浮动状态(例如 GTK3 中的 GtkMenu),要实现此目的,可以使用以下序列 used

// save floating state
gboolean was_floating = g_object_is_floating (object);
g_object_ref_sink (object);
// protected code portion

...

// restore floating state
if (was_floating)
  g_object_force_floating (object);
else
  g_object_unref (object); // release previously acquired reference

用注释替换浮动引用

您应避免将对象层次结构建立在浮动引用上,因为即使在 C 中也很难理解浮动引用,并且在使用其他 语言使用基于 GObject 的 API 时会引入其他 limitations。

在“容器”和“子项”实例之间表达所有权转移的一种方式是在文档和自省数据中使用所有权转移注释。例如,您可以实现 pattern

container_add_child (container, create_child ());

container_add_child() 定义为可以通过下沉来实现 as

/**
 * container_add_child:
 * @self: a container
 * @child: (transfer full): the child to add to the container
 *
 * ...
 */
void
container_add_child (Container *container,
                     Child *child)
{
  container->children = g_list_append (container->children, child);
}

容器不显式获取子项的引用;相反,子项的所有权被转移到容器中。语言绑定将使用转移注释来确保没有泄漏;文档工具将明确指出,被调用者现在拥有由 调用者传递的值。

使用弱引用替换浮动引用

另一种替换浮动引用的方法是在强引用处使用弱引用。`container` 可以使用 g_object_weak_ref() 对 `child` 实例获取一个弱引用。一旦 `child` 实例丢失了最后一个强引用,持有弱引用的 `container` 就会收到通知,它可以从内部列表中删除 `child`,或者将弱引用转换为强引用。