文章目錄
- 1、Gobject類定義
- 2、向Gobject系統註冊類
- 3、類的構造函數
- 4、父子類的繼承關係
- 5、類的析構函數
- 6、類的其他設置
1、Gobject類定義
Gstreamer框架是基於插件的,同時插件是可以動態的註冊、創建,gstreamer基於Gobject開發,下面來了解一下gstreamer是如何通過Gobject完成自定義類的註冊。
在每個類的c文件中,都會有以下這樣的一個宏定義:
/* class initialization */
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_INIT (gst_omx_h264_dec_debug_category, "omxh264dec", 0, \
"debug category for gst-omx video decoder base class");
G_DEFINE_TYPE_WITH_CODE (GstOMXH264Dec, gst_omx_h264_dec,
GST_TYPE_OMX_VIDEO_DEC, DEBUG_INIT);
G_DEFINE_TYPE_WITH_CODE是一個宏定義,那麼這個G_DEFINE_TYPE_WITH_CODE宏是如何向Gobject系統完成類的註冊呢?
將G_DEFINE_TYPE_WITH_CODE展開得到以下代碼:
#define G_DEFINE_TYPE_WITH_CODE(TN, t_n, T_P, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, 0) {_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 (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() */
#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
#define _G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
if (TypeName##_private_offset != 0) \
g_type_class_adjust_private_offset (klass, &TypeName##_private_offset); \
type_name##_class_init ((TypeName##Class*) klass); \
}
#else
#define _G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
type_name##_class_init ((TypeName##Class*) klass); \
}
#endif /* GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 */
從上面宏G_DEFINE_TYPE_WITH_CODE的展開式中,我們可以看出,實際是定義了幾個函數,如下所示:
static void gst_omx_h264_dec_init (GstOMXH264Dec *self);
static void gst_omx_h264_dec_class_init (GstOMXH264DecClass *klass);
static gpointer gst_omx_h264_dec_parent_class = NULL;
static gint GstOMXH264Dec_private_offset;
static void gst_omx_h264_dec_class_intern_init (gpointer klass)
{
gst_omx_h264_dec_parent_class = g_type_class_peek_parent (klass);
if (GstOMXH264Dec_private_offset != 0)
g_type_class_adjust_private_offset (klass, &GstOMXH264Dec_private_offset);
gst_omx_h264_dec_class_init ((GstOMXH264DecClass*) klass);
}
G_GNUC_UNUSED
static inline gpointer
gst_omx_h264_dec_get_instance_private (GstOMXH264Dec *self)
{
return (G_STRUCT_MEMBER_P (self, GstOMXH264Dec_private_offset));
}
GType gst_omx_h264_dec_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 (#GstOMXH264Dec),
sizeof (GstOMXH264DecClass),
(GClassInitFunc) gst_omx_h264_dec_class_intern_init,
sizeof (GstOMXH264Dec),
(GInstanceInitFunc) gst_omx_h264_dec_init,
(GTypeFlags) flags);
{ /* custom code follows */
/* following custom code */
}
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
} /* closes gst_omx_h264_dec_get_type() */
2、向Gobject系統註冊類
G_DEFINE_TYPE_WITH_CODE定義如上,那麼,最終它是如何向Gobject系統註冊該類的呢?
Gobject系統爲什麼知道你新添加了一個名叫TypeName的類,是因爲你通過g_type_register_static_simple(...)函數告訴它,我這裏有一個新類,你登記一下。
g_type_register_static_simple()函數實現如下:
GType
g_type_register_static_simple (GType parent_type,
const gchar *type_name,
guint class_size,
GClassInitFunc class_init,
guint instance_size,
GInstanceInitFunc instance_init,
GTypeFlags flags)
{
GTypeInfo info;
/* Instances are not allowed to be larger than this. If you have a big
* fixed-length array or something, point to it instead.
*/
g_return_val_if_fail (class_size <= G_MAXUINT16, G_TYPE_INVALID);
g_return_val_if_fail (instance_size <= G_MAXUINT16, G_TYPE_INVALID);
info.class_size = class_size;
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = instance_size;
info.n_preallocs = 0;
info.instance_init = instance_init;
info.value_table = NULL;
return g_type_register_static (parent_type, type_name, &info, flags);
}
從該函數實現我們可以瞭解到,向Gobject系統註冊一個類,需要告訴Gobject系統,我現在需要註冊一個新類,它父類的類型是parent_type,類型名是type_name,大小是class_size,類的初始化函數是class_init,類的實例大小以及初始化函數,還有這個類有什麼flags,通過告訴Gobject,它就會將新類登記在線。
通過G_DEFINE_TYPE_WITH_CODE宏的展開可以知道,在type_name##_get_type()函數中調用到g_type_register_static_simple()函數,從而完成了新類向Gobject系統的註冊登記。
在我們需要創建一個TestObject實例對象時,會通過調用g_object_new()函數完成,在調用g_object_new函數時,需要傳進相應的參數,這個時候,我們就將type_name##_get_type()函數的返回值傳遞給它,即演變成以下:
TestObject *testObject = (TestObject *)g_object_new (type_name##_get_type(), NULL);
而在type_name##_get_type()函數中,將會先通過g_once_init_enter()函數檢查type_name##_get_type()中的靜態變量g_define_type_idvolatile是否爲0,如果是,則通過g_type_register_static_simple()函數向Gobject系統登記TestObject類,同時返回object ID,如果g_define_type_idvolatile不爲0,則說明已經向Gobject系統註冊TestObject類,直接返回object ID,這樣,即完成了TestObject的註冊登記。
3、類的構造函數
學習C++我們都知道,類是有構造函數的,在創建類實例的時候,會自動調用該類的構造函數,那麼,在Gobject中,又是怎麼調用類的構造函數呢?
以TestObject爲例,在上面說到通過g_type_register_static_simple()函數向Gobject系統註冊自定義類的時候,就傳進了相應的參數,包括類的初始化函數type_name##_class_intern_init()以及類實例的初始化函數type_name##_init(),它們兩個共同的相當於TestObject類的構造函數。從宏定義G_DEFINE_TYPE_WITH_CODE的展開代碼中我們可以知道,在通過G_DEFINE_TYPE_WITH_CODE向Gobject系統註冊類時,還需要我們實現type_name##_class_init()和type_name##_init()函數的定義。type_name##_class_init()函數是在第一次創建TestObject類實例對象的時候調用的,該函數只會調用一次,而type_name##_init()函數則是每次創建TestObject類實例對象都會調用。
4、父子類的繼承關係
在G_DEFINE_TYPE_WITH_CODE的展開代碼中,我們可以看到以下代碼:
static gpointer type_name##_parent_class = NULL; \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
if (TypeName##_private_offset != 0) \
g_type_class_adjust_private_offset (klass, &TypeName##_private_offset); \
type_name##_class_init ((TypeName##Class*) klass); \
}
在這個宏中可以看到,定義了一個靜態的全局指針變量type_name##_parent_class,而type_name##_parent_class變量是通過g_type_class_peek_parent (klass)函數賦值的,type_name##_parent_class變量代表着什麼呢,它就是父類。一般的,會在該源文件新增一個宏,定義如下:
#define type_name##_parent_class parent_class
這樣就可以通過宏定義parent_class直接調用父類函數,而該父類,就是在通過宏定義G_DEFINE_TYPE_WITH_CODE向Gobject系統註冊類時傳進的第三個參數T_P。g_type_class_peek_parent()函數通過傳進的子類指針,查找到註冊時候的相應信息,得到父類的類型,而後通過父類類型得到父類信息並返回。
5、類的析構函數 有了相應的構造函數,在構造函數中申請了內存、硬件等資源,自然的,也會類似C++的,有相應的析構函數負責資源的釋放操作。那麼,在Gobject系統中,析構函數又是什麼回事呢?我們都知道,構造函數是從父類到子類,而析構函數是從子類到父類。在Gobject系統中的析構函數又是如何的呢?
之前說到,在通過G_DEFINE_TYPE_WITH_CODE向Gobject系統,註冊TestObject類的時候,需要定義type_name##_class_init()和type_name##_init()函數,而在類實例的初始化函數type_name##_init()中,我們可能申請了一些內存等資源,我們需要在析構函數中釋放這些資源,這個時候,需要我們在TestObject類初始化函數type_name##_class_init()覆蓋從父類繼承的析構函數,具體代碼如下:
static void
test_object_dispose (GObject * object)
{
TestObject *testobject = TEST_OBJECT (object);
/* 資源釋放*/
/* 調用父類的dispose 函數 */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
test_object_finalize (TestObject * testobject)
{
g_free(testobject->mem);
/* 調用父類的finalize 函數 */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void test_object_init(TestObject * self)
{
self->mem = g_malloc (1);
}
static void test_object_class_init(TestObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = test_object_dispose;
object_class->finalize = test_object_finalize;
...
}
由上述代碼我們可以知道,在TestObject的初始化的時候,將會覆蓋從父類繼承而來的析構函數,同時在析構函數中釋放類實例初始化時佔用的資源,同時還有遞歸調用父類的析構函數。dispose函數主要是將在類中佔用的資源釋放,而finalize函數則是有點類似真正的析構函數,將構造函數申請的資源進行釋放回收。
既然析構函數也已經有了,析構函數又會是什麼時候調用呢?
JAVA使用的是垃圾回收的機制,而Gobject則是使用引用計數的方式。當每個對象創建的時候,將會對其引用計數加一,如果期間被其他對象進行引用,也都會將它的引用計數增加;而當對象被解除引用的時候,引用計數將會減一,當引用計數減爲0的時候,將會調用對象的析構函數,進行資源的回收。
Gobject的引用計數方式大致如下:
- 使用g_object_new()函數進行實例化的時候,對象的引用計數爲1;
- 使用g_object_ref()函數進行引用對象的時候,對象的引用計數加1;
- 而當使用g_object_unref()函數解除引用的時候,對象的引用計數減1;
- 而在調用g_object_unref()函數進行解引用的時候,如果發現對象的引用計數爲0,將會先後調用該對象的dispose()函數和finalize()函數。
而爲什麼在test_object_class_init()函數中覆蓋從父類繼承過來的析構函數呢? 因爲在g_object_unref()函數中調用dispose()函數和finalize()函數是通過宏定義G_OBJECT_GET_CLASS取得OBJECT_CLASS類之後,再調用它的dispose()函數和finalize()函數,所以需要在TestObject的類初始化函數對這兩個函數指針進行覆蓋,而在TestObject類的dispose()函數和finalize()函數再通過G_OBJECT_CLASS (parent_class)取得父類指針,調用父類的析構函數。
6、類的其他設置
在Gobject系統中,設置了很多方便的宏,在使用對象的時候可以更加的方便,在相應的頭文件,一般會有如下宏定義:
typedef struct _GstTestObject TestObject;
typedef struct _GstTestObjectClass GstTestObjectClass;
/* 獲取類型 */
#define GST_TYPE_TEST_OBJECT (test_object_get_type())
/* 類實例類型判斷 */
#define GST_IS_TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TEST_OBJECT))
/* 類結構判定 */
#define GST_IS_TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TEST_OBJECT))
/* 獲取obj的類型,同時將其轉換爲GST_TYPE_TEST_OBJECT,並返回指向GstTestObjectClass的指針 */
#define GST_TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TEST_OBJECT, GstTestObjectClass))
/* 檢查obj是否是GST_TYPE_TEST_OBJECT類型,如果是,則將返回指向obj成員變量TestObject的指針 */
#define GST_TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TEST_OBJECT, TestObject))
/* 檢查klass是不是GST_TYPE_TEST_OBJECT類型,如果是,則將返回指向klass成員變量GstTestObjectClass的指針 */
#define GST_TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TEST_OBJECT, GstTestObjectClass))
/* 實例結構轉換 */
#define GST_TEST_OBJECT_CAST(obj) ((TestObject*)(obj))
struct _GstTestObject {
GstObject object;
gchar *mem;
...
}
/* 類定義 */
struct _GstTestObjectClass {
GstObjectClass object_class;
...
}
就是通過上述的宏定義,可以方便的將各種類以及對象進行轉換,在子類中可以調用父類的函數等操作,同時,在gstreamer中,還有一些屬性設置函數等,進行多樣化的類管理。
另外的,宏定義G_DEFINE_TYPE也是實現與G_DEFINE_TYPE_WITH_CODE類似的功能,G_DEFINE_TYPE_WITH_CODE可以將一些函數內置在type_name##_get_type()函數中。
沒有留言:
張貼留言