十三、GValue学习笔记
我第一次学习GValue应该是二零二一年九月九日,那是一段最美的时光。漫山遍野你的脸庞,唯有遗忘是最漫长。那时候刚接触GLib库,我在CSDN中,给这一概念放到了GLib分类中记录,其实是错误的。
GValue
是 GObject 系统中用于通用值存储的结构体。它被设计用来存储各种不同类型的值,包括基本数据类型、对象类型等。也就是说,所有GType类型系统注册的类型,都可以被GValue存储。
GValue
可以看作是一个变量容器,由类型标识符(GType)和类型的特定值组成。
要创建一个未定义的 GValue 结构体,并使用,有以下步骤注意:
-
只需创建一个零填充的 GValue 结构体即可。(因为该结构体里面有联合体,需要把所有空间置零)
-
要初始化 GValue,请使用 g_value_init() 函数。在初始化之前,GValue 不能被使用。(初始化的作用是:找到要关联GType类型的函数表,该函数表由此类型的copy,free等函数,拷贝或者初始化创建这个对象)
-
在销毁之前,你必须始终使用 g_value_unset() 来确保分配的内存被释放。(释放初始化创建的对象内存)
-
释放 GValue 结构体的内存
基本类型操作(如释放和复制)由存储在 GValue 中的类型 ID 关联的 GTypeValueTable 确定。其他 GValue 操作(如在类型之间转换值)由此接口提供。
1 GValue是GBoxed类型对象
查看Gboxed相关源文件和头文件,发现是在gobject目录下。GValue
是一个Boxed类型对象。
/* filename: gboxed.h */
#define G_TYPE_VALUE (g_value_get_type ())
/* filename: gboxed.c */
#define G_TYPE_VALUE (g_value_get_type ())
G_DEFINE_BOXED_TYPE (GValue, g_value, value_copy, value_free)
static GValue *
value_copy (GValue *src_value)
{
GValue *dest_value = g_new0 (GValue, 1);
if (G_VALUE_TYPE (src_value))
{
g_value_init (dest_value, G_VALUE_TYPE (src_value));
g_value_copy (src_value, dest_value);
}
return dest_value;
}
static void
value_free (GValue *value)
{
if (G_VALUE_TYPE (value))
g_value_unset (value);
g_free (value);
}
2 GValue结构体定义
既然知道是GBoxed对象,那系统就没有自动申请内存和释放内存空间函数。
下面是 GValue
定义的结构体:
struct _GValue
{
/*< private >*/
GType g_type; /* GValue存储的数据的数据类型 */
/* public for GTypeValueTable methods */
union {
gint v_int;
guint v_uint;
glong v_long;
gulong v_ulong;
gint64 v_int64;
guint64 v_uint64;
gfloat v_float;
gdouble v_double;
gpointer v_pointer;
} data[2];
};
- data:存储数据
3 GValue 使用
具体示例代码可以参考 /assets/GObjectStudy/202311/13_GValue/
3.1 创建一个零填充的 GValue 结构体
因为不是标准GObject对象,所以没有自动分配和释放内存函数。必须用零填充是因为使用了联合体。
栈区创建:
GValue a = G_VALUE_INIT; /* #define G_VALUE_INIT { 0, { { 0 } } } */
堆区创建:
GValue *value = g_new0 (GValue, 1);
3.2 GValue初始化函数g_value_init
/* filename:gvalue.c */
GValue*
g_value_init (GValue *value,
GType g_type)
{
GTypeValueTable *value_table;
/* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL); be more elaborate below */
g_return_val_if_fail (value != NULL, NULL);
/* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL); be more elaborate below */
value_table = g_type_value_table_peek (g_type);
if (value_table && G_VALUE_TYPE (value) == 0)
{
/* setup and init */
value_meminit (value, g_type);
value_table->value_init (value);
}
else if (G_VALUE_TYPE (value))
g_critical ("%s: cannot initialize GValue with type '%s', the value has already been initialized as '%s'",
G_STRLOC,
g_type_name (g_type),
g_type_name (G_VALUE_TYPE (value)));
else /* !G_TYPE_IS_VALUE (g_type) */
g_critical ("%s: cannot initialize GValue with type '%s', %s",
G_STRLOC,
g_type_name (g_type),
value_table ? "this type is abstract with regards to GValue use, use a more specific (derived) type" : "this type has no GTypeValueTable implementation");
return value;
}
如果第一步没有用零填充结构体,G_VALUE_TYPE (value) == 0
就不能通过。
关键函数为 value_meminit (value, g_type)
和 value_table->value_init (value)
3.2.1 value_meminit (value, g_type)
static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */
value_meminit (GValue *value,
GType value_type)
{
value->g_type = value_type;
memset (value->data, 0, sizeof (value->data));
}
3.2.2 value_table->value_init (value)
value_table->value_init (value)
具体指向那些函数,和类型注册函数有关,GObject
类型对象一般都用的同一个 GTypeValueTable
。GBoxed
类型对象也如此。
/* filename: gboxed.c */
static void
boxed_proxy_value_init (GValue *value)
{
value->data[0].v_pointer = NULL;
}
GType
g_boxed_type_register_static (const gchar *name,
GBoxedCopyFunc boxed_copy,
GBoxedFreeFunc boxed_free)
{
static const GTypeValueTable vtable = {
boxed_proxy_value_init,
boxed_proxy_value_free,
boxed_proxy_value_copy,
boxed_proxy_value_peek_pointer,
"p",
boxed_proxy_collect_value,
"p",
boxed_proxy_lcopy_value,
};
GTypeInfo type_info = {
0, /* class_size */
NULL, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
&vtable, /* value_table */
};
GType type;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (boxed_copy != NULL, 0);
g_return_val_if_fail (boxed_free != NULL, 0);
g_return_val_if_fail (g_type_from_name (name) == 0, 0);
type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
/* install proxy functions upon successful registration */
if (type)
_g_type_boxed_init (type, boxed_copy, boxed_free);
return type;
}
GObject
对象类型 GTypeValueTable
/* filename: gobject.c */
static const GTypeValueTable value_table = {
g_value_object_init, /* value_init */
g_value_object_free_value, /* value_free */
g_value_object_copy_value, /* value_copy */
g_value_object_peek_pointer, /* value_peek_pointer */
"p", /* collect_format */
g_value_object_collect_value, /* collect_value */
"p", /* lcopy_format */
g_value_object_lcopy_value, /* lcopy_value */
};
一般对象类型,都是把 value->data[0].v_pointer = NULL,也就是使用的是指针变量。
3.3 g_value_unset
/* filename: gvalue.c */
void
g_value_unset (GValue *value)
{
GTypeValueTable *value_table;
if (value->g_type == 0)
return;
g_return_if_fail (value);
value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
g_return_if_fail (value_table);
if (value_table->value_free)
value_table->value_free (value);
memset (value, 0, sizeof (*value));
}
/* filename: gboxed.c */
static void
boxed_proxy_value_free (GValue *value)
{
if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
/*如果是通过拷贝函数*/
_g_type_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
}
3.4 释放GValue结构体内存
如果通过堆区创建的GValue结构体
g_free (value)
4 补充
4.1 为什么用户一定要编写GBoxed拷贝和释放函数
因为 g_value_set_boxed
调用 value_set_boxed_internal
。
下面函数使用了 g_boxed_copy
。
static inline void
value_set_boxed_internal (GValue *value,
gconstpointer boxed,
gboolean need_copy,
gboolean need_free)
{
if (!boxed)
{
/* just resetting to NULL might not be desired, need to
* have value reinitialized also (for values defaulting
* to other default value states than a NULL data pointer),
* g_value_reset() will handle this
*/
g_value_reset (value);
return;
}
if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
g_boxed_free (G_VALUE_TYPE (value), value->data[0].v_pointer);
value->data[1].v_uint = need_free ? 0 : G_VALUE_NOCOPY_CONTENTS;
value->data[0].v_pointer = need_copy ? g_boxed_copy (G_VALUE_TYPE (value), boxed) : (gpointer) boxed;
}