文章目录
GStreamer是依附于GLib 2.0对象模型的,采用了信号与对象属性的机制,所有的GStreamer对象都采用GObject继承的方法进行扩展,是所有GStreamer对象的基类,因此有必要学习一下Gobject是怎么回事。
Gobject简介
GObject是一个程序库,它可以让我们使用C语言编写面向对象的程序。面向对象只是一种编程思想,不是一种编程语言;GObject 告诉我们;使用 C 语言编写程序时,也可以用面向对象的编程思想。
在GObject系统中,对象由三个部分组成:
- 对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);
- 对象的类结构(唯一,结构型,由对象的所有实例共同拥有);
- 对象的实例(多个,结构型,对象的具体实现)。
GObject 类具有以下功能:
- 基于引用计数的内存管理
- 实现对象的构造函数与析构函数
- 可设置对象属性的 set/get 函数
- 易于使用的信号机制
GObject中如何实现类定义
在 GObject 世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。
例如:MyObject 是实例结构体,MyObjectClass 是类结构体,它们合起来便可以称为 MyObject类
#include <glib-object.h>
typedef struct _MyObjectClass
{
GObjectClass parent_class;
} MyObjectClass;
typedef struct _MyObject
{
GObject parent_instance;
} MyObject;
G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT);
在GObject中一个对象的产生遵循如下原则:
- 如果产生的是该类的第一个实例,那么先分配Class结构体,再分配针对该实例的结构体;否则直接分配针对该实例的结构;也就是说类结构体中的内容,是该类生成的所有实例对象所公有的;而实例化每个对象时,为其单独分配专门的实例对象结构体。
- MyObject类的实例结构体第一个成员是GObject 结构体;类结构体第一个成员是GObjectClass 结构体。GObject结构体与 GObjectClass 结构体分别是 GObject类实例结构体与类结构体;当它们分别作为 MyObject类的实例结构体与类结构体的第一个成员时;这意味着 MyObject类类继承自 GObject类
- 要使用Gobject对象系统,必须在自定义的类结构体与实例结构体中,第一个成员分别是GObjectClass 结构体与GObject 结构体
- 类结构体初始化函数只被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。这意味着,所有对象共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。
注册自定义GObject类
要向GObject系统中注册自定义GObject类,GObject向我们提供了一个简单的宏来完成这个任务:G_DEFINE_TYPE。如上例所示的:G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT)
次宏定义如下:
/**
* G_DEFINE_TYPE:
* @TN: The name of the new type, in Camel case.
* @t_n: The name of the new type, in lowercase, with words
* separated by '_'.
* @T_P: The #GType of the parent type.
*
* A convenience macro for type implementations, which declares a class
* initialization function, an instance initialization function (see #GTypeInfo
* for information about these) and a static variable named `t_n_parent_class`
* pointing to the parent class. Furthermore, it defines a *_get_type() function.
* See G_DEFINE_TYPE_EXTENDED() for an example.
*
* Since: 2.4
*/
#define G_DEFINE_TYPE(TN, t_n, T_P) G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
#define G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, _f_) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \
\
static void type_name##_init (TypeName *self); \
static void type_name##_class_init (TypeName##Class *klass); \
static gpointer type_name##_parent_class = NULL; \
static gint TypeName##_private_offset; \
\
_G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
\
G_GNUC_UNUSED \
static inline gpointer \
type_name##_get_instance_private (const TypeName *self) \
{ \
return (G_STRUCT_MEMBER_P (self, TypeName##_private_offset)); \
} \
\
GType \
type_name##_get_type (void) \
{ \
static volatile gsize g_define_type_id__volatile = 0; \
if (g_once_init_enter (&g_define_type_id__volatile)) \
{ \
GType g_define_type_id = \
g_type_register_static_simple (TYPE_PARENT, \
g_intern_static_string (#TypeName), \
sizeof (TypeName##Class), \
(GClassInitFunc) type_name##_class_intern_init, \
sizeof (TypeName), \
(GInstanceInitFunc) type_name##_init, \
(GTypeFlags) flags); \
{ /* custom code follows */
#define _G_DEFINE_TYPE_EXTENDED_END() \
/* following custom code */ \
} \
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
} \
return g_define_type_id__volatile; \
} /* closes type_name##_get_type() */
G_DEFINE_TYPE宏是怎么完成向GObject系统中注册自定义类
以G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT)
为例,将宏定义展开如下:
static void my_object_init(MyObject * self);
static void my_object_class_init(MyObjectClass * klass);
static gpointer my_object_parent_class = ((void *) 0);
static void my_object_class_intern_init(gpointer klass)
{
my_object_parent_class = g_type_class_peek_parent(klass);
my_object_class_init((MyObjectClass *) klass);
}
GType my_object_get_type(void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter(&g_define_type_id__volatile))
{
GType g_define_type_id = g_type_register_static_simple(((GType) ((20) << (2))),
g_intern_static_string("MyObject"),
sizeof(MyObjectClass),
(GClassInitFunc) my_object_class_intern_init,
sizeof(MyObject),
(GInstanceInitFunc) my_object_init,
(GTypeFlags) 0);
}
return g_define_type_id__volatile;
};
- GObject 类型系统之所以能够接受 MyObject 这个类型,完全拜
my_object_get_type
函数所赐;因为my_object_get_type函数调用了g_type_register_static_simple
函数,此函数由GObject类型系统提供,其主要职责就是为 GObject 类型系统注册新的类型。 - my_object_get_type 第一次被调用时,会向 GObject 类型系统注册 MyObject 类型,然后对 MyOject 类进行实例化;产生实例结构体对象,最后返回对象的数据类型的 ID;从
my_object_get_type
第二次被调用开始,它就只进行 MyOject 类的实例化,不会再重复向 GObject 类型系统注册类型。 my_object_get_type
会被g_object_new
调用,所有 GObject 类派生的类型,皆可由g_object_new
函数进行实例化,例如:MyObject *my_obj = g_object_new(my_object_get_type(), NULL)
- 由上面的代码中可以看出来,如果在.c文件中声明一个G_DEFINE_TYPE宏的,就会自动生成一个my_object_get_type (void)函数,,因此需要在.h头文件中声明。G_DEFINE_TYPE 声明了两个函数,但是并没有实现,需要定义对象的用户自己去实现,这两个函数是:
static void my_object_init(MyObject * self);
static void my_object_class_init(MyObjectClass * klass);
这两个函数是对象的初始化函数,相当于C++中的构造函数,第一个函数在每个对象创建的时候都会被调用,第二个函数只有在第一次创建对象的时候才会被调用。
要完成自定义的 GObject 类,还必须遵循GObject的一些规范,创建一些宏定义
//返回GType关联的对象类型
#define MY_OBJECT_TYPE (my_object_get_type ())
//返回一个指向MY_OBJECT类型的指针。这个宏用于必要时安全地强制转换一个静态类型。运行环境检查时,同样也是安全地执行动态类型。
#define MY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_OBJECT_TYPE, MyObject))
//它以类结构的动态类型检查来进行静态转换,并返回一个指向MY_OBJECT_CLASS这个类型的类结构的指针。
#define MY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_OBJECT_TYPE, MyObjectClass))
// 这个宏用于判断输入的对象实例是否是是MY_OBJECT_TYPE类型
#define IS_MY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_OBJECT_TYPE))
//输入的类型指针是否是MY_OBJECT_TYPE类型
#define GST_IS_MY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_OBJECT_TYPE))
C代码中实现Gobject类步骤
- 在 .h 文件中包含 glib-object.h
- 在 .h 文件中构建实例结构体与类结构体,并分别将 GObject 类的实例结构体与类结构体置于第一个成员;
- 在 .h 文件中定义 PREFIX_OBJECT_TYPE宏,并声明 prefix_object_get_type函数,prefix为自定义类名
- 在 .h 文件中继续完成上面那5个宏定义;
- 在 .c 文件中调用 G_DEFINE_TYPE 宏产生类型注册代码。
自定义GObject类构造函数
G_DEFINE_TYPE 宏为我们声明了类的类型初始化函数与类的实例的初始化函数:
static void my_object_init(MyObject * self)
static void my_object_class_init(MyObjectClass * klass)
- my_object_class_init:是类的类型初始化函数,作用是使得用户能够在类的类型初始化阶段插入一些额外的功能。
- my_object_init:是类的实例的初始化函数,可以将它理解为 C++ 对象的构造函数。
在通过G_DEFINE_TYPE向Gobject系统注册类,还需要我们自行实现my_object_class_init
和my_object_init
函数。
my_object_class_init
:函数是在第一次创建MyObject类实例对象的时候调用的,该函数只会调用一次,test_object_init
:函数则是每次创建TestObject类实例对象都会调用。
自定义GObject类析构函数
在通过G_DEFINE_TYPE向Gobject系统,注册MyObject类的时候,需要定义my_object_class_init()和my_object_init()函数,而在类实例的初始化函数my_object_init()中,我们可能申请了一些内存等资源,我们需要在析构函数中释放这些资源,这个时候,需要我们在MyObject类初始化函数test_object_class_init中覆盖从父类继承的析构函数,具体代码如下:
static void
my_object_dispose (GObject * object)
{
MyObject *myobject = My_OBJECT (object);
/* 资源释放*/
/* 调用父类的dispose 函数 */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void my_object_finalize (MyObject * myobject)
{
g_free(myobject->mem);
/* 调用父类的finalize 函数 */
G_OBJECT_CLASS (parent_class)->finalize (myobject);
}
static void my_object_init(MyObject * self)
{
self->mem = g_malloc (1);
}
static void my_object_class_init(MyObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = my_object_dispose;
object_class->finalize = my_object_finalize;
...
}
- MyObject的初始化的时候,将会覆盖从父类继承而来的析构函数,同时在析构函数中释放类实例初始化时占用的资源,同时还有递归调用父类的析构函数。
- dispose函数主要是将在类中占用的资源释放,而finalize函数则是有点类似真正的析构函数,将构造函数申请的资源进行释放回收。
析构函数何时调用呢?
Gobject的引用计数方式大致如下
- 使用g_object_new()函数进行实例化的时候,对象的引用计数为1;
- 使用g_object_ref()函数进行引用对象的时候,对象的引用计数加1;
- 使用g_object_unref()函数解除引用的时候,对象的引用计数减1;
在调用g_object_unref()函数进行解引用的时候,如果发现对象的引用计数为0,将会先后调用该对象的dispose()函数和finalize函数。
为什么需要在my_object_class_init()函数中覆盖从父类继承过来的析构函数呢?
因为在g_object_unref()函数中调用dispose()函数和finalize函数是通过宏定义G_OBJECT_GET_CLASS取得OBJECT_CLASS类之后,再调用它的dispose()函数和finalize函数,所以需要在MyObject的类初始化函数中,对这两个函数指针进行覆盖,而在MyObject类的dispose()函数和finalize函数再通过G_OBJECT_CLASS (parent_class)取得父类指针,调用父类的析构函数。
感觉写的不怎么样,可能我的理解还不够深刻吧,后续在学习GObject对象系统的一些其它机制。