何时取消引用具有浮动引用的 GVariant?
Posted
技术标签:
【中文标题】何时取消引用具有浮动引用的 GVariant?【英文标题】:When to unref a GVariant that has a floating reference? 【发布时间】:2015-06-22 03:28:34 【问题描述】:https://developer.gnome.org/glib/unstable/glib-GVariant.html#g-variant-ref-sink
我已经阅读了上面的 glib 手册,上面写着:“GVariant
使用浮动引用计数系统。所有名称以 g_variant_new_
开头的函数都返回浮动引用。”但实际在哪里什么是浮动引用计数的描述?我找不到它的全面描述。
我特别想了解何时需要取消引用变体以及何时不需要。例如:
GVariant *a_v = g_variant_new_boolean(TRUE);
GVariant *another_v = g_variant_new("v", a_v);
-
我想我不需要取消引用
a_v
,因为它已被第二个g_variant_new
消耗。对吗?
我是否需要取消引用 another_v
(假设从那时起 another_v
没有传递给其他任何东西)?
这是在哪里记录的? (我认为通过从搜索过程中发现的不同示例进行推断,我有正确的理解,但似乎找不到能清楚解释这一点的官方 glib 文档)。
【问题讨论】:
【参考方案1】:GObject 参考手册中有一个关于floating references 的部分进行了更详细的介绍。浮动引用可能看起来有点晦涩,但它们对 C 语言非常有用,所以花几分钟时间来真正理解它们是个好主意。
我假设您了解引用计数的工作原理 - 如果没有大量文档,请先花几分钟阅读。
首先,让我们看看如果g_variant_new_boolean
返回常规引用,您的示例会发生什么。当您第一次获得该值时,引用计数将为 1。当您将其传递给 g_variant_new
时,g_variant_new
会将引用计数增加到 2。在某些时候,我假设您将处理 another_v
,此时指出a_v
的引用计数将降至 1……但请记住,直到引用计数达到 0 才会释放内存。
为了解决这个问题,您有两种选择。第一个是让g_variant_new
窃取调用者的引用,这基本上是一个糟糕的解决方案。当您调用g_variant_new
(或任何类似函数)时,您会放弃您的参考,因此您以后每次想要将其传递给其他东西时都需要手动参考a_v
。
另一种选择是在完成后手动取消引用它。这不是世界末日,但很容易忘记做事或出错(比如忘记在错误路径中取消引用)。
GVariant 所做的是返回一个“浮动”引用。考虑它的最简单方法(恕我直言)是g_variant_ref
第一次被调用它并没有真正做任何事情 - 它只是“沉没”浮动参考。引用计数从 1 变为 1。随后调用 g_variant_ref
,但是会增加引用计数。
现在让我们看看您的示例实际发生了什么。 g_variant_new_boolean
返回一个浮动引用。然后将它传递给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
)。
【讨论】:
很好的解释。感谢您花时间详细解释。 能否请您解释一下“并且当 another_v 的引用计数达到 0 时,a_v 的引用计数将减少”。 another_v 的引用计数何时会达到 0? 每当其他代码调用g_variant_unref(another_v)
。基本上,当容器 (another_v
) 被销毁时,它也会减少 a_v
的引用计数。由于在调用之前该 refcount 为 1,因此将其递减会将其减少为 0,这意味着它将被释放。重新阅读第 3 段(描述 非 浮动引用会发生什么)可能会有所帮助。【参考方案2】:
引用计数系统在GObject的手册中有解释,特别是在Object Memory Management部分。
何时使用它可能取决于您的应用程序(变量的所有权将如何工作)。
这个想法类似于 i-node 在 Unix/Linux 中处理文件时的工作方式。文件是一个对象,位于存储的特定块中。每当您创建指向该文件的符号链接时,该文件由一个额外的文件拥有(引用计数增加)。每当您删除符号链接时,引用计数都会减少。当没有任何东西拥有该对象时,可以将其销毁(或者可以将空间还给系统)。
如果你销毁了一个对象,并且没有任何东西链接那个对象,你就不能再使用它了。如果您的对象可能有多个所有者,那么您可能想要使用引用计数,因此当其中一个所有者删除计数器时,该对象不会被销毁......直到最后一个所有者销毁它。
【讨论】:
感谢您的指点和解释。但两者似乎都是关于“完整”引用计数。我了解引用计数的这一方面。我不完全理解的是具体的“浮动”引用计数。只是为了说明存在完整与浮动引用计数的概念,来自 glib 手册:“在具有浮动引用的 GVariant 上调用 g_variant_ref_sink() 会将浮动引用转换为完整引用。” 确实,它们是关于完全引用计数的。我确实弄错了你的问题:-)【参考方案3】:GObject 参考手册中有一节关于浮动引用的内容更详细。浮动引用可能看起来有点晦涩,但它们对 C 语言非常有用,所以花几分钟时间来真正理解它们是个好主意。
我假设您了解引用计数的工作原理 - 如果没有大量文档,请先花几分钟阅读。
首先,让我们看看如果 g_variant_new_boolean 返回常规引用,您的示例会发生什么。当您第一次获得该值时,引用计数将为 1。当您将其传递给 g_variant_new 时,g_variant_new 会将引用计数增加到 2。在某些时候,我假设您将处理 another_v,此时 a_v 的引用计数将下降到 1……但请记住,直到引用计数达到 0 才会释放内存。
为了解决这个问题,您有两种选择。首先是让 g_variant_new 窃取调用者的引用,这基本上是一个糟糕的解决方案。当您调用 g_variant_new(或任何类似函数)时,您会放弃您的参考,因此您以后每次想要将它传递给其他东西时都需要手动 ref a_v。
另一种选择是在完成后手动取消引用它。这不是世界末日,但很容易忘记做事或出错(比如忘记在错误路径中取消引用)。
GVariant 所做的是返回一个“浮动”引用。考虑它的最简单方法(恕我直言)是第一次调用 g_variant_ref 时它并没有真正做任何事情——它只是“沉没”浮动 ref。引用计数从 1 变为 1。但是,对 g_variant_ref 的后续调用将增加引用计数。
现在让我们看看您的示例实际发生了什么。 g_variant_new_boolean 返回一个浮动引用。然后将它传递给 g_variant_new,它调用 g_variant_ref,它接收浮动引用。引用计数现在为 1,当 another_v 的 refcount 达到 0 时,a_v 的 refcount 将递减,在这种情况下达到 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)。
【讨论】:
以上是关于何时取消引用具有浮动引用的 GVariant?的主要内容,如果未能解决你的问题,请参考以下文章