GObject库确实记录了很多。很难确定所创建实体的目的。即,我没有GValue
,GTypeValueTable
,GTypeInfo
,GParamSpec
和TypeData
的角色。
简而言之,类型注册的过程如下。每种类型都由TypeNode
结构表示。 TypeNode
结构有2种存储:static_fundamental_type_nodes array
用于存储静态基本类型的TypeNodes
,static_type_nodes_ht
哈希表用于静态非基本类型。如果是非基本类型,则每个GType
只是相应TypeNode
的内存地址;如果是基本类型,则每个TypeNode
只是static_fundamental_type_nodes
中GValue
的索引。动态类型会发生什么-我不知道,如果可以,请向我解释。相应的代码位于gtype_init函数中,负责初始化类型系统http://git.gnome.org/browse/glib/tree/gobject/gtype.c#n4323。GParamSpec
,GObject
和GTypes
本身就是GValue
,因此它们被注册为类型。GParameters
用于通过它注册新的类型值,但是如何呢?
似乎需要GParamSpec
和GObject
来注册GTypeValueTable
类型(不确定)。到底是怎么做到的?各自的作用是什么?
最重要:GTypeInfo
,TypeData
和TypeData
的作用是什么? TypeNode
由GTypeValueTable
引用,并包含GTypeValueTable
以及子结构BoxedData,ClassData,IFaceData,InstanceData(为什么是Instance,我们不注册类型吗?)。而且,它们似乎彼此重复,因为它们全部包含对base_init/finalize的引用,class_init/finalize对ojit_code的引用。
因此,GObject爸爸,如果您正在阅读此书,请自己解释!描述您使用这些结构的目的。
最佳答案
除非您尝试使用一些非常低级的代码,否则您真正需要关心的仅有两个是GValue和GParamType
我将从GParamType
开始GParamType
用于向GObject注册属性。举例来说,我有一个名为Person的GObject子类,并且我希望它具有两个属性:Name和Age。在class_init
函数中,我将像这样注册这些
{
GParamSpec *pspec;
. . .
pspec = g_param_spec_string ("name", "Name", "The name of the person", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_NAME, pspec);
pspec = g_param_spec_int ("age", "Age", "The age of the person", 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_AGE, spec);
. . .
}
现在,您可以在这些属性上调用
g_object_get
或g_object_set
,系统将知道如何处理它char *name;
int age;
g_object_set (G_OBJECT (person), "name", "Steve", "age", 37, NULL);
g_object_get (G_OBJECT (person), "name", &name, "age", &age, NULL);
g_print ("%s is %d years old\n", name, age);
// And because the type system knows when a property is a string, it knows how to give
// you a copy of the string, so you need to free it once you've finished with it
g_free (name);
此处解释了各种参数:GParamSpec所有标准类型都有GValue类型:字符串, bool 值,整数等,其他一些库(例如GStreamer)将注册自己的自定义变量。
除了在GObjectClass上安装属性之外,您很少需要处理GParamSpec。它们出现的两个主要场合是在GObjectClass的set/get_property方法和GObject通知信号中。在最后一种情况下,通过调用
g_param_spec_get_name
来检测哪个属性已接收到通知信号很有用,但实际上最好使用更具体的通知信号,如下所示:g_signal_connect (person, "notify::name", G_CALLBACK (name_changed_cb), NULL);
g_signal_connect (person, "notify::age", G_CALLBACK (age_changed_cb), NULL);
而不是
g_signal_connect (person, "notify", G_CALLBACK (something_changed_cb), NULL);
有时您可能想创建自己的结构并将其用于属性。例如,如果我有
struct _PersonDetails {
char *name;
int age;
}
而不是在Person对象上具有两个属性,我想要一个称为“详细信息”的属性。 GLib类型系统不知道如何处理我的自定义
struct _PersonDetails
,因此我需要为其创建一个盒装类型,以便它知道如何在Glib内部传递结构时正确地复制/释放该结构。这就是GValue
出现的地方。GValue
用于包装不同类型的值,以便可以正确地复制和释放它们(如果需要),并可以使用泛型函数。例如,GObjectClass方法set_property具有以下原型(prototype):
void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
这意味着可以传入可以由GValue表示的任何类型,并且不需要诸如set_int_property,set_string_property,set_bool_property之类的特定功能。
这也意味着函数
g_object_set
和g_object_get
知道如何处理传入的参数,因为它知道属性“name”已注册为字符串类型,并且具有复制/释放该字符串所需的功能。有关GValue的更多信息,请参见-Generic values
要在GLib类型系统中注册我们的自定义
struct _PersonDetails
,我们将创建一个自定义Boxed类型,该类型告诉系统如何复制和释放它。详细信息在这里:Boxed TypesG_DEFINE_BOXED_TYPE (PersonDetails, person_details,
person_details_copy,
person_details_free)
. . .
static gpointer
person_details_copy (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
struct _PersonDetails *copy = g_new (struct _PersonDetails, 1);
// We need to copy the string
copy->name = g_strdup (details->name);
copy->age = details->age;
return (gpointer) copy;
}
static void
person_details_free (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
// name was allocated so it needs freed as well
g_free (details->name);
g_free (details);
}
现在我们可以使用注册类型
pspec = g_param_spec_boxed ("details", "Details", "The person's details", person_details_get_type (), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_DETAILS, pspec);
关于gtk - GValue,GTypeValueTable,GTypeInfo和GParamSpec的用途,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14840456/