gobject
简介
- Gobject 系统提供了一个灵活的、可扩展的、并且容易映射到其他语言的面向对象的 C 语言框架.
(1)是Glib库的动态类型系统实现,它实现了:
[1] 基于引用计数的内存管理
[2] 实例的构造和析构
[3] 通用的set/get的属性获取方法
[4] 简单易用的信号机制 - GObject 的动态类型系统允许程序在运行时进行类型注册,主要目的有两个:
(1)使用面向对象的设计方法来编程,GObject 仅依赖于 GLib 和 libc , 通过它可使用纯 C 语言设计一整套面向对象的软件模块。
(2)多语言交互,GObject 框架很容易连结其它语言,包括 C++ 、 Java 、 Ruby 、 Python 和 .NET/Mono 等。 - GObject世界里,类是两个结构体的组合:实例结构体,类结构体。
(1)类结构体初始化函数一般被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。
(2)所有实例共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。 - Gtype 类型系统是 Glib 运行时类型认证和管理系统。
(1)Gtype API 是 Gobject 系统的 基础 ,它提供注册和管理所有基本数据、用户定义对象和接口类型的技术实现。
[1] G_DEFINE_TYPE 宏、 G_DEFINE_INTERFACE 宏、 g_type_register_static 函数等都在 GType 实现。
[2] G_DEFINE_TYPE 宏 主要用于实现用户定义类型 ,包括:
a. 声明类初始化函数、声明实例初始化函数、声明父类的一些信息
b. 用于获取分配类型 ID 的 xx_xx_get_type () 函数 - 对象实例化
(1)g_object_new的功能家族:用于实例化从GObject的基类型,继承的任何的GType。
[1] 确保类和实例结构已经被GLib的类型系统正确地初始化,然后在其它地方调用构造函数类方法:
a. 调用g_type_create_instance分配并清空内存
b. 根据构造参数初始化对象实例
GTYPE实现基本类型
编译命令:gcc gtype.c $(pkg-config --cflags --libs gobject-2.0) -o gtype
*.h
#include <glib-object.h>
// a. 用struct来创建实例对象和类对象,实现“C风格”的对象
typedef struct _SomeObject SomeObject;
struct _SomeObject {
GTypeInstance gtype;
gint m_a;
gchar* m_b;
gfloat m_c;
};
// “类结构体”定义所有的方法函数,类对象将是共享的
typedef struct _SomeObjectClass SomeObjectClass;
struct _SomeObjectClass {
GTypeClass gtypeclass;
void (*method1) (SomeObject *self, gint);
void (*method2) (SomeObject *self, gchar*);
};
// b. 声明一个"get_type"函数,第一次调用该函数时,负责向系统注册对象的类型,返回GType类型值,此后的调用会直接返回该GType值。
// 该值实际上是系统用来区别已注册类型的整型数字。
GType some_object_get_type (void);
// c. 声明一些用来管理对象生命期的函数:初始化时创建对象的函数,结束时销毁对象的函数。
void some_object_class_init (gpointer g_class, gpointer class_data);
void some_object_class_final (gpointer g_class, gpointer class_data);
void some_object_instance_init (GTypeInstance *instance, gpointer g_class);
// d. 用上面我们约定的方式来命名成员方法函数。
void some_object_method1 (SomeObject *self, gint); /* virtual */
void some_object_method2 (SomeObject *self, gchar*); /* virtual */
void some_object_method3 (SomeObject *self, gfloat); /* non-virtual */
// e. 创建一些样板式代码,符合规则的同时也让事情更简单一些
#define SOME_OBJECT_TYPE (some_object_get_type ())
#define SOME_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOME_OBJECT_TYPE, SomeObject))
#define SOME_OBJECT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), SOME_OBJECT_TYPE, SomeObjectClass))
#define SOME_IS_OBJECT(obj) (G_TYPE_CHECK_TYPE ((obj), SOME_OBJECT_TYPE))
#define SOME_IS_OBJECT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), SOME_OBJECT_TYPE))
#define SOME_OBJECT_GET_CLASS(obj)(G_TYPE_INSTANCE_GET_CLASS ((obj), SOME_OBJECT_TYPE, SomeObjectClass))
*.c
#include "gtype.h"
// a. 实现虚方法。
void some_object_method1_impl (SomeObject *self, gint a)
{
self->m_a = a;
g_print ("Method1: %i.\n", self->m_a);
}
void some_object_method2_impl (SomeObject *self, gchar* b)
{
self->m_b = b;
g_print ("Method2: %s.\n", self->m_b);
}
// b. 实现所有公有方法。,我们。
// 实现虚方法,必须使用“GET_CLASS”宏来从类型系统中获取到类对象,用以调用虚函数表中的虚方法
void some_object_method1 (SomeObject *self, gint a) {
SOME_OBJECT_GET_CLASS (self)->method1 (self, a);
}
void some_object_method2 (SomeObject *self, gchar* b) {
SOME_OBJECT_GET_CLASS (self)->method2 (self, b);
}
// 非虚方法,直接写实现代码即可。
void some_object_method3 (SomeObject *self, gfloat c) {
self->m_c = c;
g_print ("Method3: %f\n", self->m_c);
}
// c. 实现初始化/销毁方法
// 该函数将在类对象创建时被调用,传入的参数是指向该对象的泛型指针,使用它前须转型为合适的类型
void some_object_class_init(gpointer g_class, gpointer class_data) {
g_print ("some_object_class_init start.\n");
SomeObjectClass *this_class = SOME_OBJECT_CLASS (g_class);
/* 填写类结构体的方法成员 (本例只存在一个虚函数表) */
this_class->method1 = &some_object_method1_impl;
this_class->method2 = &some_object_method2_impl;
g_print ("some_object_class_init end.\n\n");
}
/* 该函数在类对象不再被使用时调用 */
void some_object_class_final (gpointer g_class, gpointer class_data) {
g_print ("some_object_class_final end.\n\n");
}
/* 该函数在实例对象被创建时调用。系统通过g_class实例的类来传递该实例的类。 */
void some_object_instance_init (GTypeInstance *instance, gpointer g_class) {
g_print ("some_object_instance_init start.\n");
SomeObject *this_object = SOME_OBJECT (instance);
/* 填写实例结构体中的成员变量 */
this_object->m_a = 123;
this_object->m_b = NULL;
this_object->m_c = 4.56;
g_print ("some_object_instance_init end.\n\n");
}
// d. 实现能够返回给调用者SomeObject的GType的函数。第一次运行时向系统注册SomeObject来获取GType,GType保存在静态变量中
// 虽然可以使用一个独立的函数来注册该类型,但这样的实现可以保证类在使用前是注册过的,该函数通常在实例化第一个对象时被调用。
// 因为该类没有父类,所以父类函数是空的
GType some_object_get_type (void) {
g_print ("some_object_get_type start.\n");
static GType type = 0;
if (type == 0) {
/* 这是系统用来完整描述要注册的类型是如何被创建、初始化和销毁的结构体。 */
static const GTypeInfo type_info = {
sizeof (SomeObjectClass),
NULL, /* 父类初始化函数 */
NULL, /* 父类销毁函数 */
some_object_class_init, /* 类对象初始化函数 */
some_object_class_final, /* 类对象销毁函数 */
NULL, /* 类数据 */
sizeof (SomeObject),
0, /* 预分配的字节数 */
some_object_instance_init /* 实例对象初始化函数 */
};
/* 因为我们的类没有父类,所以它将被认为是“基础类(fundamental)”,
因此必须要告诉系统,该类既是一个复合结构的类(与浮点型,整型,
或者指针不同),而且是可以被实例化的(系统可以创建实例对象,相反如接口
或者抽象类则不能被实例化) */
static const GTypeFundamentalInfo fundamental_info = { G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE };
type = g_type_register_fundamental(
g_type_fundamental_next (), /* 下一个可用的GType */
"SomeObjectType", /* 类型的名称 */
&type_info, /* 上面定义的type_info */
&fundamental_info, /* 上面定义的fundamental_info */
0); /* 类型不是抽象的 */
}
g_print ("some_object_get_type stop: type=%ld.\n\n", type);
return type;
}
int main()
{
// 让系统创建实例对象, g_type_create_instance的使用仅保留给基本类型的实现者,否则不建议使用
// GObject层次结构的实例应该通过g_object_new创建
GTypeInstance *g_instance = g_type_create_instance (some_object_get_type());
SomeObject *testobj = SOME_OBJECT (g_instance);
/* 调用我们定义了的方法 */
if (testobj) {
g_print ("init value: m_a=%d\n", testobj->m_a);
some_object_method1 (testobj, 32);
g_print ("init value: m_b=%s\n", testobj->m_b);
some_object_method2 (testobj, "Hello world.");
g_print ("init value: m_c=%.2f\n", testobj->m_c);
some_object_method3 (testobj, 6.9);
}
g_type_free_instance(g_instance);
g_print ("some_object_get_type stop: .\n\n");
return 0;
}
gobject使用 sample
流程解析
- PMDList *list = g_object_new (PM_TYPE_DLIST, NULL);
(1)GType pm_dlist_get_type (void);
(2)#define PM_TYPE_DLIST (pm_dlist_get_type ()) - G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
(1)GObject 库所提供的 G_DEFINE_TYPE 宏可以帮助生成 pm_dlist_get_type 函数的实现代码。
(2)可以让 GObject 库的数据类型系统能够识别我们所定义的 PMDList 类类型
(3)三个参数: 1. 类名,即 PMDList;2. 类的成员函数名称的前缀,例如 pm_dlist_*;3. PMDList 类类型的父类型 - G_TYPE_OBJECT
(1)PT 格式,P 是项目名称缩写,T 是数据类型的名称:
(2)g_object_get_type 形式的函数 统称为 p_t_get_type 函数
(3)PM_TYPE_DLIST 和 G_TYPE_OBJECT 形式的宏 统称为 P_TYPE_T 宏
simple code
*.h
#ifndef PM_DLIST_H
#define PM_DLIST_H
#include <glib-object.h>
#define PM_TYPE_DLIST (pm_dlist_get_type ())
typedef struct _PMDListNode PMDListNode;
struct _PMDListNode {
PMDListNode *prev;
PMDListNode *next;
void *data;
};
typedef struct _PMDList PMDList;
struct _PMDList {
GObject parent_instance;
PMDListNode *head;
PMDListNode *tail;
};
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass {
GObjectClass parent_class;
};
GType pm_dlist_get_type (void);
#endif
*.c
#include "pm-dlist.h"
G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
static void pm_dlist_init (PMDList *self) {
g_print ("\tpm_dlist_init.\n");
self->head = NULL;
self->tail = NULL;
}
static void pm_dlist_class_init (PMDListClass *klass) {
g_print ("pm_dlist_class_init.\n");
}
int main (void)
{
PMDList *list = g_object_new (PM_TYPE_DLIST, NULL);
if (G_IS_OBJECT (list)) // 检查实例是否为 GObject 对象
g_print ("\tthis is a GObject.\n");
g_object_unref (list);
return 0;
}
GObject 子类对象的私有属性(隐藏数据)
*.h
#ifndef PM_DLIST_H
#define PM_DLIST_H
#include <glib-object.h>
#define PM_TYPE_DLIST (pm_dlist_get_type ())
typedef struct _PMDList PMDList;
struct _PMDList {
GObject parent_instance;
};
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass {
GObjectClass parent_class;
};
GType pm_dlist_get_type (void);
#endif
*.c
#include "pm-dlist.h"
G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
#define PM_DLIST_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PM_TYPE_DLIST, PMDListPrivate))
typedef struct _PMDListNode PMDListNode;
struct _PMDListNode {
PMDListNode *prev;
PMDListNode *next;
void *data;
};
typedef struct _PMDListPrivate PMDListPrivate;
struct _PMDListPrivate {
PMDListNode *head;
PMDListNode *tail;
};
static void pm_dlist_init (PMDList *self) {
g_print ("\tpm_dlist_init.\n");
PMDListPrivate *priv = PM_DLIST_GET_PRIVATE (self);
priv->head = NULL;
priv->tail = NULL;
}
static void pm_dlist_class_init (PMDListClass *klass) {
g_print ("pm_dlist_class_init.\n");
g_type_class_add_private (klass, sizeof (PMDListPrivate));
}
int main (void)
{
PMDList *list = g_object_new (PM_TYPE_DLIST, NULL);
g_object_unref (list);
return 0;
}
GObject 属性设置
*.h
#ifndef PM_DLIST_H
#define PM_DLIST_H
#include <glib-object.h>
#include <glib/gstdio.h>
#define PM_TYPE_DLIST (pm_dlist_get_type ())
typedef struct _PMDList PMDList;
struct _PMDList {
GObject parent_instance;
};
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass {
GObjectClass parent_class;
};
GType pm_dlist_get_type (void);
#endif
*.c
#include "pm-dlist.h"
G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
#define PM_DLIST_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PM_TYPE_DLIST, PMDListPrivate))
typedef struct _PMDListNode PMDListNode;
struct _PMDListNode {
PMDListNode *prev;
PMDListNode *next;
void *data;
};
typedef struct _PMDListPrivate PMDListPrivate;
struct _PMDListPrivate {
PMDListNode *head;
PMDListNode *tail;
};
static void pm_dlist_init (PMDList *self) {
g_print ("\tpm_dlist_init.\n");
PMDListPrivate *priv = PM_DLIST_GET_PRIVATE (self);
priv->head = NULL;
priv->tail = NULL;
}
enum PropertyDList {
PROP_0,
PROP_DLIST_ENABLE,
PROP_DLIST_TEXT,
PROP_DLIST_PRMS,
PROP_DLIST_PRMS_ARRAY,
PROP_DLIST_PRMS_STR
};
static void pm_dlist_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
switch (prop_id) {
case PROP_DLIST_ENABLE:
g_print("enable: %d.\n", g_value_get_boolean(value));
break;
case PROP_DLIST_TEXT:
g_value_get_string(value);
break;
case PROP_DLIST_PRMS_ARRAY: {
GArray *array = g_value_get_boxed (value);
guint element_size = g_array_get_element_size (array);
for (guint i=0; i < array->len; i++) {
guint my_int = g_array_index (array, guint, i);
g_print("g_array_index: element_size=%u, %uth=%u.\n", element_size, i, my_int);
g_assert (my_int == i);
}
break;
}
case PROP_DLIST_PRMS_STR: {
GString *str_get = g_value_get_boxed (value);
g_print("GString: %s.\n", str_get->str);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void pm_dlist_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
switch (prop_id) {
case PROP_DLIST_ENABLE:
g_value_set_boolean(value, 1);
break;
case PROP_DLIST_TEXT:
g_value_set_string(value, "test");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void pm_dlist_class_init (PMDListClass *klass) {
g_print ("pm_dlist_class_init.\n");
g_type_class_add_private (klass, sizeof (PMDListPrivate));
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->set_property = pm_dlist_set_property;
gobject_class->get_property = pm_dlist_get_property;
g_object_class_install_property(gobject_class, PROP_DLIST_ENABLE,
g_param_spec_boolean("dlist-enable", "dlist enable", "dlist enable",
FALSE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_DLIST_TEXT,
g_param_spec_string("dlist-text", "DLIST text", "text",
NULL, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_DLIST_PRMS_ARRAY,
g_param_spec_boxed("dlist-prms-array", "dlist prms", "dlist prms",
G_TYPE_ARRAY, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_DLIST_PRMS_STR,
g_param_spec_boxed("dlist-prms-str", "dlist prms", "dlist prms",
G_TYPE_GSTRING, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}
// settings = g_settings_new ("org.gtk.test.binding");
// g_settings_set_boolean (settings, "bool", FALSE);
int main (void)
{
PMDList *list = g_object_new (PM_TYPE_DLIST, NULL);
g_assert (G_IS_OBJECT (list));
g_object_set(G_OBJECT(list), "dlist-enable", FALSE, NULL);
// g_value_get_boxed GString
GValue value_str = G_VALUE_INIT;
g_value_init (&value_str, G_TYPE_GSTRING);
g_assert (G_VALUE_HOLDS_BOXED (&value_str));
GString *str = g_string_new ("bla");
g_value_take_boxed (&value_str, str);
g_object_set_property(G_OBJECT(list), "dlist-prms-str", &value_str);
g_value_unset (&value_str); // 不需要释放,否则段错误。g_string_free (str, TRUE);
// g_value_get_boxed GArray
GValue value_array = G_VALUE_INIT;
g_value_init (&value_array, G_TYPE_ARRAY);
g_assert (G_VALUE_HOLDS_BOXED (&value_array));
GArray *int_array = g_array_new (TRUE, FALSE, sizeof (guint));
for (guint i = 0; i < 5; i++)
g_array_append_val (int_array, i);
g_value_take_boxed (&value_array, int_array);
g_object_set_property(G_OBJECT(list), "dlist-prms-array", &value_array);
g_array_unref (int_array); // 是否不需要?
g_value_unset (&value_array);
g_object_unref (list);
return 0;
}