前言
最近閱讀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
建立一個例項變數,然後將變數的引用計數置為1g_object_ref
將其引用計數加1g_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學習筆記(二)型別建立與註冊