2023年1月8日 星期日

Gobject類對象

文章目錄

  • 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()函數中。

沒有留言:

張貼留言