GObject學習筆記(一)類和例項

paw5zx發表於2024-11-17

前言

最近閱讀Aravis原始碼,其中大量運用了GObject,於是打算學習一下。

此係列筆記僅主要面向初學者,不會很深入探討原始碼的細節,專注於介紹GObject的基本用法。

此係列筆記參考GObject Tutorial for beginners

本文可在個人部落格中閱讀,體驗更加

套個盾:文中定義的名詞只是為了更好地理解GObject,不具備權威性。

類和例項

在GObject中,每個可例項化類型別都與兩個結構體相關聯:一個是類結構體,一個是例項結構體。

  • 類結構體會被註冊到型別系統中(具體註冊方式在下一節討論),在g_object_new首次呼叫時,型別系統會檢查相應的類結構體是否已經被初始化為一個類變數,沒有則建立並初始化。此後所有該類的例項變數都將共享這個已初始化的類變數。每個類變數只會被建立一次。
  • 每次呼叫g_object_new時都會建立例項變數。

在GObject系統中,類結構體和例項結構體都會被例項化,在記憶體中佔有特定的空間。為了便於描述,我們將分配給類結構體的例項稱為“類變數”,而分配給例項結構體的例項稱為“例項變數”。

GObject例項的結構體定義如下

//file: gobject.h
typedef struct _GObject  GObject;
struct  _GObject
{
  GTypeInstance  g_type_instance;
  
  /*< private >*/
  guint          ref_count;  /* (atomic) */
  GData         *qdata;
};

GObject類的結構體定義如下(我們可以先不用瞭解結構的細節):

//file: gobject.h
typedef struct _GObjectClass             GObjectClass;
struct  _GObjectClass
{
  GTypeClass   g_type_class;

  /*< private >*/
  GSList      *construct_properties;

  /*< public >*/
  /* seldom overridden */
  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);
  /* overridable methods */
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
  void       (*dispose)			(GObject        *object);
  void       (*finalize)		(GObject        *object);
  /* seldom overridden */
  void       (*dispatch_properties_changed) (GObject      *object,
					     guint	   n_pspecs,
					     GParamSpec  **pspecs);
  /* signals */
  void	     (*notify)			(GObject	*object,
					 GParamSpec	*pspec);

  /* called when done constructing */
  void	     (*constructed)		(GObject	*object);

  /*< private >*/
  gsize		flags;

  gsize         n_construct_properties;

  gpointer pspecs;
  gsize n_pspecs;

  /* padding */
  gpointer	pdummy[3];
};

下面使用一個簡單示例,來演示GObject的類和例項的使用

//file: example01.c
#include <glib-object.h>

int main (int argc, char **argv) 
{

    GObject* instance1,* instance2;     //指向例項的指標
    GObjectClass* class1,* class2;      //指向類的指標
   
    instance1 = g_object_new (G_TYPE_OBJECT, NULL);
    instance2 = g_object_new (G_TYPE_OBJECT, NULL);
    g_print ("The address of instance1 is %p\n", instance1);
    g_print ("The address of instance2 is %p\n", instance2);
 
    class1 = G_OBJECT_GET_CLASS (instance1);
    class2 = G_OBJECT_GET_CLASS (instance2);
    g_print ("The address of the class of instance1 is %p\n", class1);
    g_print ("The address of the class of instance2 is %p\n", class2);
 
    g_object_unref (instance1);
    g_object_unref (instance2);

    return 0;
}

其中:

  • g_object_new函式建立例項變數並返回指向它的指標。在例項變數第一次被建立之前,它對應的類變數也會被建立並初始化。
  • 引數G_TYPE_OBJECT是GObject基類的型別識別符號,這是GObject型別系統的核心,所有其他GObject型別都從這個基型別派生。
  • G_OBJECT_GET_CLASS返回指向引數所屬類變數的指標
  • g_object_unref會銷燬例項變數並釋放記憶體。

輸出:

The address of instance1 is 0x55d3ddc05600
The address of instance2 is 0x55d3ddc05620
The address of the class of instance1 is 0x55d3ddc05370
The address of the class of instance2 is 0x55d3ddc05370

可以發現,兩個例項變數的地址不同,但兩個例項變數對應的類變數的地址相同,因為兩個例項變數共享一個類變數

引用計數

引用計數機制的概念在此不做介紹

在GObject中,GObject例項具有引用計數機制:

//file: example02.c
#include <glib-object.h>
 
static void show_ref_count (GObject* instance) 
{
    if (G_IS_OBJECT (instance))
        /* Users should not use ref_count member in their program. */
        /* This is only for demonstration. */
        g_print ("Reference count is %d.\n", instance->ref_count);
    else
        g_print ("Instance is not GObject.\n");
}
 
int main (int argc, char **argv) 
{
    GObject* instance;

    instance = g_object_new (G_TYPE_OBJECT, NULL);
    g_print ("Call g_object_new.\n");
    show_ref_count (instance);
    g_object_ref (instance);
    g_print ("Call g_object_ref.\n");
    show_ref_count (instance);
    g_object_unref (instance);
    g_print ("Call g_object_unref.\n");
    show_ref_count (instance);
    g_object_unref (instance);
    g_print ("Call g_object_unref.\n");
    g_print ("Now the reference count is zero and the instance is destroyed.\n");
    g_print ("The instance memories are possibly returned to the system.\n");
    g_print ("Therefore, the access to the same address may cause a segmentation error.\n");

    return 0;
}

其中:

  • g_object_new建立一個例項變數,然後將變數的引用計數置為1
  • g_object_ref將其引用計數加1
  • g_object_unref將引用計數減1,如果此時引用計數為0,則析構變數。

輸出:

Call g_object_new.
Reference count is 1.
Call g_object_ref.
Reference count is 2.
Call g_object_unref.
Reference count is 1.
Call g_object_unref.
Now the reference count is zero and the instance is destroyed.
The instance memories are possibly returned to the system.
Therefore, the access to the same address may cause a segmentation error.

初始化和析構過程

GObject初始化和銷燬的實際過程比較複雜。以下是簡單的描述,不做詳細說明.

初始化

1.用型別系統註冊GObject型別。這是在呼叫main函式之前的GLib的初始化過程中完成的。(如果編譯器是gcc,則__attribute__ ((constructor))用於限定初始化函式。)
2.為GObjectClass和GObject結構分配記憶體
3.初始化GObjectClass結構記憶體。這個記憶體將是GObject的類變數。
4.初始化GObject結構記憶體。這個記憶體將是GObject的例項變數。

上述初始化過程在第一次呼叫g_object_new函式時執行。在第二次及後續呼叫g_object_new時,它只執行兩個過程:①為GObject結構分配記憶體②初始化記憶體。

析構

1.銷燬GObject例項。釋放例項的記憶體

GObject變數型別是靜態型別。靜態型別永遠不會破壞它的類。因此,即使被銷燬的例項變數是最後一個,類變數仍然存在,直到程式終止。

參考文章

1.GObject Tutorial for beginners

推薦

下一篇:GObject學習筆記(二)型別建立與註冊

相關文章