何时取消引用具有浮动引用的 GVariant?

When to unref a GVariant that has a floating reference?

https://developer.gnome.org/glib/unstable/glib-GVariant.html#g-variant-ref-sink

我读过上面的 glib 手册,上面写着:“GVariant 使用浮动引用计数系统。所有名称以 g_variant_new_ return 开头的函数都是浮动的references." 但是浮动引用计数的实际描述在哪里?我找不到对它的全面描述。

特别是我想了解何时需要取消引用变体,何时不需要。例如:

GVariant *a_v = g_variant_new_boolean(TRUE);
GVariant *another_v = g_variant_new("v", a_v);
  1. 我想我不需要取消引用 a_v 因为它被第二个 g_variant_new 消耗了。对吗?
  2. 我是否需要取消引用 another_v(假设从那时起 another_v 没有传递给其他任何东西)?
  3. 这是在哪里记录的? (我认为通过在搜索过程中发现的不同示例进行推断,我有正确的理解,但似乎无法找到清楚解释这一点的官方 glib 文档)。

引用计数系统在GObject, in particular, in the section Object Memory Management的手册中有解释。

何时使用它可能取决于您的应用程序(变量的所有权将如何工作)。

这个想法类似于 Unix/Linux 在处理文件时 i-node 的工作方式。文件是一个对象,位于存储中的特定块中。每当您创建指向该文件的符号链接时,该文件 拥有 一个额外的文件(引用计数增加)。每当您删除符号链接时,引用计数都会减少。当没有任何东西拥有该对象时,就可以销毁它(或者 space 可以还给系统)。

如果你销毁了一个对象,而没有任何东西链接到那个对象,你就不能再使用它了。如果您的对象可能有多个所有者,那么您可能想要使用引用计数,因此当其中一个所有者删除计数器时,该对象不会被销毁......直到最后一个所有者销毁它。

GObject 参考手册中有一个关于 floating references 的部分,其中更详细一些。浮动引用可能看起来有点晦涩难懂,但它们对 C 确实非常有用,因此花几分钟时间真正理解它们是个好主意。

我假设您了解引用计数的工作原理——如果没有大量文档,请先花几分钟阅读一下。

首先,让我们看看如果 g_variant_new_boolean return 编辑一个常规引用​​,您的示例会发生什么。当您第一次获取该值时,引用计数将为 1。当您将其传递给 g_variant_new 时,g_variant_new 会将引用计数增加到 2。在某些时候,我假设您将处理 another_v,此时 a_v 的引用计数将下降到 1……但请记住,在引用计数达到 0 之前不会释放内存。

为了解决这个问题,您有两种选择。第一个是让 g_variant_new 窃取调用者的引用,这基本上是一种糟糕的解决方案。当你调用 g_variant_new (或任何类似的函数)时你放弃了你的引用,所以以后你每次想将它传递给其他东西时都需要手动引用 a_v

另一种选择是在完成后手动取消引用。这不是世界末日,但很容易忘记做或出错(比如忘记在错误路径中取消引用)。

GVariant 所做的是 return 一个 "floating" 引用。最简单的理解方式(恕我直言)是第一次 g_variant_ref 被调用时它实际上并没有做任何事情——它只是 "sinks" 浮动引用。引用计数从 1 变为 1。随后 调用 g_variant_ref,但是,将增加引用计数。

现在让我们看看您的示例实际发生了什么。 g_variant_new_boolean return 是浮动引用。然后,您将它传递给 g_variant_new,后者调用 g_variant_ref,后者接收浮动引用。引用计数现在为 1,当 another_v 的引用计数达到 0 时 a_v 的引用计数将减少,在这种情况下达到 0 并且所有内容都将被释放。不用你打电话 g_variant_unref.

不过,关于浮动引用的最酷的部分是像这样的事情会发生什么:

GVariant *a_v = g_variant_new_boolean(TRUE);
GVariant *another_v = g_variant_new("v", a_v);
GVariant *yet_another_v = g_variant_new("v", a_v);

当第二次调用 g_variant_new 时,a_v 的引用计数将再次增加(到 2)。在第二次将 a_v 传递给 g_variant_new 之前不需要调用 g_variant_ref — 第一次调用看起来就像第一次,并且一致性是 API 中一个非常好的特性。

此时可能很明显,但是是的,您确实需要在 another_v 上调用 g_variant_unref(并且在最后一个示例中,yet_another_v)。

GObject 参考手册中有一个关于浮动引用的部分,其中有更详细的介绍。浮动引用可能看起来有点晦涩难懂,但它们对 C 确实非常有用,因此花几分钟时间真正理解它们是个好主意。

我假设您了解引用计数的工作原理——如果没有大量文档,请先花几分钟阅读一下。

首先,让我们看看如果 g_variant_new_boolean return 编辑一个常规引用​​,您的示例会发生什么。当您第一次获取该值时,引用计数将为 1。当您将其传递给 g_variant_new 时,g_variant_new 会将引用计数增加到 2。在某些时候,我假设您将处理 another_v,此时 a_v 的引用计数将下降到 1……但请记住,在引用计数达到 0 之前不会释放内存。

为了解决这个问题,您有两种选择。第一个是让 g_variant_new 窃取调用者的引用,这基本上是一种糟糕的解决方案。当你调用 g_variant_new (或任何类似的函数)时你放弃了你的引用,所以以后你每次想将它传递给其他东西时都需要手动引用 a_v。

另一种选择是在完成后手动取消引用。这不是世界末日,但很容易忘记做或出错(比如忘记在错误路径中取消引用)。

GVariant 所做的是 return 一个“浮动”引用。最简单的理解方式(恕我直言)是 g_variant_ref 第一次被调用时它实际上并没有做任何事情——它只是“下沉”了浮动引用。引用计数从 1 变为 1。但是,后续调用 g_variant_ref 会增加引用计数。

现在让我们看看您的示例实际发生了什么。 g_variant_new_boolean return 是一个浮动引用。然后,您将它传递给 g_variant_new,它调用 g_variant_ref,它接收浮动引用。引用计数现在为 1,当 another_v 的引用计数达到 0 时 a_v 的引用计数将减少,在这种情况下达到 0 并且所有内容都将被释放。不用你打电话 g_variant_unref.

不过,关于浮动引用的最酷的部分是像这样的事情会发生什么:

GVariant *a_v = g_variant_new_boolean(TRUE);
GVariant *another_v = g_variant_new("v", a_v);
GVariant *yet_another_v = g_variant_new("v", a_v);

当第二次调用 g_variant_new 时,a_v 的引用计数将再次增加(到 2)。在第二次将 a_v 传递给 g_variant_new 之前不需要调用 g_variant_ref — 第一次调用看起来就像第一次,并且一致性是 API 中一个非常好的特性。

此时可能很明显,但是是的,您确实需要在 another_v 上调用 g_variant_unref(并且,在最后一个示例中,yet_another_v)。