本文引用自http://imtx.cn,原文作者:TualatriX
原文:http://library.gnome.org/devel/gobject/2.14/chapter-gobject.html
前面两个章节讨论了Glib动态类型系统的细节和它的信号控制系统。GObject库同时也包括了一个最基本的类型即基类,即为GObject。
GObject是一个类化的可实例的基类。它实现了:
- 使用引用计数的内存管理。
- 实例的构造和析构 。
- 使用set/get函数对进行的一般性属性操作。
- 简易使用的信号机制。
所有使用GLib类型系统的GNOME库(如GTK+和GStreamer)都由GObject继承,所以了解有关它是如何工作的细节是如此重要。
对象实例
g_object_new系列函数可以被用作实例化由GObject基本类型继承的一些GType。所有的这些函数确保类和实例的结构被Glib的类型系统正确的初始化,然后调用一个或别的构造类方法用于:
- 通过g_type_create_instance来分配和清理内存。
- 初始化对象的实例用构造属性。
尽管你可以期待所有的类和实例成员(指向父类的部分)都会设置为0,但是尽量考虑自己明确地去设置它们。
由GObject继承的对象们允许重载它的类构造方法:在做下面之前它们需要连接到父类的构造方法:
GObject* (*constructor) (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties);
下面的代码显示MamanBar重载了父类的构造器:
#define MAMAN_TYPE_BAR (maman_bar_get_type ()) #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) typedef struct _MamanBar MamanBar; typedef struct _MamanBarClass MamanBarClass; struct _MamanBar { GObject parent; /* instance members */ }; struct _MamanBarClass { GObjectClass parent; /* class members */ }; /* used by MAMAN_TYPE_BAR */ GType maman_bar_get_type (void); static GObject * maman_bar_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *obj; { /* Invoke parent constructor. */ MamanBarClass *klass; GObjectClass *parent_class; klass = MAMAN_BAR_CLASS (g_type_class_peek (MAMAN_TYPE_BAR)); parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); obj = parent_class->constructor (type, n_construct_properties, construct_properties); } /* do stuff. */ return obj; } static void maman_bar_instance_init (GTypeInstance *instance, gpointer g_class) { MamanBar *self = (MamanBar *)instance; /* do stuff */ } static void maman_bar_class_init (gpointer g_class, gpointer g_class_data) { GObjectClass *gobject_class = G_OBJECT_CLASS (g_class); MamanBarClass *klass = MAMAN_BAR_CLASS (g_class); gobject_class->constructor = maman_bar_constructor; } GType maman_bar_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (MamanBarClass), NULL, /* base_init */ NULL, /* base_finalize */ maman_bar_class_init, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ sizeof (MamanBar), 0, /* n_preallocs */ maman_bar_instance_init /* instance_init */ }; type = g_type_register_static (G_TYPE_OBJECT, "MamanBarType", &info, 0); } return type; }
如果用户是用下面的方法创建一个MamanBar的实例的:
MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);
如果这是用户创建的第一个实例,那么maman_b_class_init函数将会在maman_b_base_class_init 调用后才调用。这将确保这个新对象的类结构会正确的初始化。这里,mana_bar_class_init被期望重载对象的类方法,并且设置这个类的自己的方法。在上面的例子中,构造方法是唯一被重载的方法:它设置为了maman_bar_constructor。
一旦g_object_new已经得到了一个初始化后的类结构的引用,它就调用它的构造方法来为这个新对象创建一个实例。因为它刚刚被maman_bar_class_init由maman_bar_constructor重载,后者将被调用,因为它是实现正确的,它也连接了父类的构造器。问题是,我们怎么才能找到父类的构造器。一个接近(在GTK+源代码中使用)将会来保存原始的构造器在一个从maman_class_init来的静态变量中,然后可以通过maman_bar_constructor来重用它。这是足够清楚的,也非常简单,但是我说这不是很好的办法,更好的办法是用g_type_class_peek和g_type_class_peek_parent函数。
最终,在长链中一个g_object_constructor被最后一个构造器调用。这个函数通过g_type_create_instance分配的对象实例的缓存,这意味着如果它被注册过的话instance_init函数将在这点上被调用。在instance_init返回以后,对象就完全初始化好了,应该准备好应答用户的请求了。当g_type_create_instance返回了,g_object_constructor设置构造属性(属性由g_object_new给的)并返回用户的构造器,使它来允许完成一些有用的实例初始化。
这个所描述的处理过程看起来有一点难懂(在我看来确实难懂),但是由下面的表格可以清楚的概况起来,当g_object_new调用时,相关函数的调用顺序。
箭头表示由g_object_new进行的函数调用和它们的调用顺序。
表格太大,不贴了
读者可能会对函数调用的顺序感到不安:当然,技术上来说, 类结构方法总是在GType的instance_init调用之前被调用(因为g_type_create_instance来调用instance_init相当于由g_object_constructor来调用顶级类的构造器,这是用户所期望的),运行着用户提供的构造器的用户的代码将总是在GType的instance_init函数之后运行,因为用户提供的构造器必须在做一些有用的事情前被链接好。