The GObject base class
GObject是一个fundamental classed instantiatable type,它的功能如下:
- 内存管理
- 构建/销毁实例
- set/get属性方法
- 信号
/**
* GObjectClass:
* @g_type_class: the parent class
* @constructor: the @constructor function is called by g_object_new () to
* complete the object initialization after all the construction properties are
* set. The first thing a @constructor implementation must do is chain up to the
* @constructor of the parent class. Overriding @constructor should be rarely
* needed, e.g. to handle construct properties, or to implement singletons.
* @set_property: the generic setter for all properties of this type. Should be
* overridden for every type with properties. If implementations of
* @set_property don't emit property change notification explicitly, this will
* be done implicitly by the type system. However, if the notify signal is
* emitted explicitly, the type system will not emit it a second time.
* @get_property: the generic getter for all properties of this type. Should be
* overridden for every type with properties.
* @dispose: the @dispose function is supposed to drop all references to other
* objects, but keep the instance otherwise intact, so that client method
* invocations still work. It may be run multiple times (due to reference
* loops). Before returning, @dispose should chain up to the @dispose method
* of the parent class.
* @finalize: instance finalization function, should finish the finalization of
* the instance begun in @dispose and chain up to the @finalize method of the
* parent class.
* @dispatch_properties_changed: emits property change notification for a bunch
* of properties. Overriding @dispatch_properties_changed should be rarely
* needed.
* @notify: the class closure for the notify signal
* @constructed: the @constructed function is called by g_object_new() as the
* final step of the object creation process. At the point of the call, all
* construction properties have been set on the object. The purpose of this
* call is to allow for object initialisation steps that can only be performed
* after construction properties have been set. @constructed implementors
* should chain up to the @constructed call of their parent class to allow it
* to complete its initialisation.
*
* The class structure for the GObject type.
*
* |[<!-- language="C" -->
* // Example of implementing a singleton using a constructor.
* static MySingleton *the_singleton = NULL;
*
* static GObject*
* my_singleton_constructor (GType type,
* guint n_construct_params,
* GObjectConstructParam *construct_params)
* {
* GObject *object;
*
* if (!the_singleton)
* {
* object = G_OBJECT_CLASS (parent_class)->constructor (type,
* n_construct_params,
* construct_params);
* the_singleton = MY_SINGLETON (object);
* }
* else
* object = g_object_ref (G_OBJECT (the_singleton));
*
* return object;
* }
* ]|
*/
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:
*
* The base object type.
*
* All the fields in the `GObject` structure are private to the implementation
* and should never be accessed directly.
*
* Since GLib 2.72, all #GObjects are guaranteed to be aligned to at least the
* alignment of the largest basic GLib type (typically this is #guint64 or
* #gdouble). If you need larger alignment for an element in a #GObject, you
* should allocate it on the heap (aligned), or arrange for your #GObject to be
* appropriately padded. This guarantee applies to the #GObject (or derived)
* struct, the #GObjectClass (or derived) struct, and any private data allocated
* by G_ADD_PRIVATE().
*/
struct _GObject
{
GTypeInstance g_type_instance;
/*< private >*/
guint ref_count; /* (atomic) */
GData *qdata;
};
Object instantiation
g_object_new()族能够实例化任何继承自GObject的GType。族中所有函数都能保证将类结构和实例结构在GLib的类型系统中正确初始化,之后会在某个时机调用类的constructor方法。
类的constructor方法的作用如下:
- 通过g_type_create_instance()函数分配并清空内存。
- 使用构造属性初始化对象实例。
static GObject*
g_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
/* create object */
object = (GObject*) g_type_create_instance (type);
/* set construction parameters */
if (n_construct_properties)
{
GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, FALSE);
/* set construct properties */
while (n_construct_properties--)
{
GValue *value = construct_params->value;
GParamSpec *pspec = construct_params->pspec;
construct_params++;
object_set_property (object, pspec, value, nqueue, TRUE);
}
g_object_notify_queue_thaw (object, nqueue);
/* the notification queue is still frozen from g_object_init(), so
* we don't need to handle it here, g_object_newv() takes
* care of that
*/
}
return object;
}
GObject能够确保所有类结构和实例结构的成员(除指向父结构的成员外)的值都被设置为0。
当所有构造相关的工作都完成,构造属性都被设置完成后,最后会调用constructed方法。
继承自GObject的对象都能重写类的constructed方法。举例如下:
#define VIEWER_TYPE_FILE viewer_file_get_type ()
G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)
struct _ViewerFile
{
GObject parent_instance;
/* instance members */
char *filename;
guint zoom_level;
};
/* will create viewer_file_get_type and set viewer_file_parent_class */
G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT)
static void
viewer_file_constructed (GObject *obj)
{
/* update the object state depending on constructor properties */
/* Always chain up to the parent constructed function to complete object
* initialisation. */
G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}
static void
viewer_file_finalize (GObject *obj)
{
ViewerFile *self = VIEWER_FILE (obj);
g_free (self->filename);
/* Always chain up to the parent finalize function to complete object
* destruction. */
G_OBJECT_CLASS (viewer_file_parent_class)->finalize (obj);
}
static void
viewer_file_class_init (ViewerFileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = viewer_file_constructed;
object_class->finalize = viewer_file_finalize;
}
static void
viewer_file_init (ViewerFile *self)
{
/* initialize the object */
}
通过g_object_new(VIEWER_TYPE_FILE,NULL)的方式,第一次实例化ViewerFile对象时,会调用view_file_base_init函数,接着调用view_file_class_init函数。这样新对象的类结构就能够被初始化完成。
当g_object_new获取到新对象的初始化完成的类结构的索引之后,如果GObject的constructor函数被重写,那么,g_object_new函数将会调用新类型的类结构中constructor(如上例中的viewer_file_cosntructed,实际上也是新类ViewFileClass中GObject的constructor,这是因为,ViewFile是一个final类型对象)来创建新对象。
gpointer
g_object_new (GType object_type,
const gchar *first_property_name,
...)
{
GObject *object;
......
/* short circuit for calls supplying no properties */
if (!first_property_name)
return g_object_new_with_properties (object_type, 0, NULL, NULL);
......
return object;
}
GObject *
g_object_new_with_properties (GType object_type,
guint n_properties,
const char *names[],
const GValue values[])
{
GObjectClass *class, *unref_class = NULL;
GObject *object;
......
class = g_type_class_peek_static (object_type);
if (class == NULL)
class = unref_class = g_type_class_ref (object_type);
if (n_properties > 0)
{
......
}
else
object = g_object_new_internal (class, NULL, 0);
......
return object;
}
static gpointer
g_object_new_internal (GObjectClass *class,
GObjectConstructParam *params,
guint n_params)
{
......
GObject *object;
......
if G_UNLIKELY (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
return g_object_new_with_custom_constructor (class, params, n_params);
object = (GObject *) g_type_create_instance (class->g_type_class.g_type);
......
}
static gpointer
g_object_new_with_custom_constructor (GObjectClass *class,
GObjectConstructParam *params,
guint n_params)
{
......
GObject *object;
......
/* construct object from construction parameters */
object = class->constructor (class->g_type_class.g_type, class->n_construct_properties, cparams);
......
return object
}
Chaining up to its parent
在继承Gobject的新类型的初始化过程中,如上所述,会存在GObjectClass的constructor被用户改写的情况,那么,用户为新对象设置的constructor与GObjectClass的默认constructor之间是什么关系呢?
如下图所示:
如图,有两个类结构,GObjectClass和TstrClass,两个类都有各自的finalize函数,TstrClass的finalize函数用于销毁Tstr 实例结构中与其本身相关的数据(不包含Tstr结构中其父结构GObjectClass中的数据),TStrClass的finalize函数在最后会调用其父类GObjectClass的finalize函数来销毁GObject中的数据。这期间有个函数调用关系,这个调用关系就是图示“chain up”的含义。
我们在gtk库中找个finalize函数来具体说明一下。
static void
gtk_print_backend_cups_finalize (GObject *object)
{
GtkPrintBackendCups *backend_cups;
GTK_DEBUG (PRINTING, "CUPS Backend: finalizing CUPS backend module");
backend_cups = GTK_PRINT_BACKEND_CUPS (object);
g_free (backend_cups->default_printer);
backend_cups->default_printer = NULL;
gtk_cups_connection_test_free (backend_cups->cups_connection_test);
backend_cups->cups_connection_test = NULL;
g_hash_table_destroy (backend_cups->auth);
g_free (backend_cups->username);
#ifdef HAVE_COLORD
g_object_unref (backend_cups->colord_client);
#endif
g_clear_object (&backend_cups->avahi_cancellable);
g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
g_clear_object (&backend_cups->dbus_connection);
g_clear_object (&backend_cups->secrets_service_cancellable);
if (backend_cups->secrets_service_watch_id != 0)
{
g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
}
g_list_free_full (backend_cups->temporary_queues_in_construction, g_free);
backend_cups->temporary_queues_in_construction = NULL;
g_list_free_full (backend_cups->temporary_queues_removed, g_free);
backend_cups->temporary_queues_removed = NULL;
backend_parent_class->finalize (object);
}
现在我们可以回到我们之前的问题,类似于finalize,constructor也有“chain up“的关系存在。
同样的,我们在gtk库中找一个constructor函数看看。
static GObject*
gtk_button_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
GtkButton *button;
object = (* G_OBJECT_CLASS (gtk_button_parent_class)->constructor) (type,
n_construct_properties,
construct_params);
button = GTK_BUTTON (object);
button->constructed = TRUE;
if (button->label_text != NULL)
gtk_button_construct_child (button);
return object;
}
回到我们之前的话题,在初始化过程中,通过"chain up"会调用到g_object_constructor,只不过constructor的调用顺序与finalize相反。因为,我们需要g_object_constructor函数调用g_type_create_instance来为我们的新类型实例分配空间。同时,instance_init函数也会在此时执行。instance_init的返回,也意味这新类型的初始化过程完成。之后,用户就能使用新类型。
GTypeInstance*
g_type_create_instance (GType type)
{
TypeNode *node;
GTypeInstance *instance;
GTypeClass *class;
gchar *allocated;
gint private_size;
gint ivar_size;
guint i;
node = lookup_type_node_I (type);
......
class = g_type_class_ref (type);
/* We allocate the 'private' areas before the normal instance data, in
* reverse order. This allows the private area of a particular class
* to always be at a constant relative address to the instance data.
* If we stored the private data after the instance data this would
* not be the case (since a subclass that added more instance
* variables would push the private data further along).
*
* This presents problems for valgrindability, of course, so we do a
* workaround for that case. We identify the start of the object to
* valgrind as an allocated block (so that pointers to objects show up
* as 'reachable' instead of 'possibly lost'). We then add an extra
* pointer at the end of the object, after all instance data, back to
* the start of the private area so that it is also recorded as
* reachable. We also add extra private space at the start because
* valgrind doesn't seem to like us claiming to have allocated an
* address that it saw allocated by malloc().
*/
private_size = node->data->instance.private_size;
ivar_size = node->data->instance.instance_size;
......
allocated = g_malloc0 (private_size + ivar_size);
instance = (GTypeInstance *) (allocated + private_size);
for (i = node->n_supers; i > 0; i--)
{
TypeNode *pnode;
pnode = lookup_type_node_I (node->supers[i]);
if (pnode->data->instance.instance_init)
{
instance->g_class = pnode->data->instance.class;
pnode->data->instance.instance_init (instance, class);
}
}
......
instance->g_class = class;
if (node->data->instance.instance_init)
node->data->instance.instance_init (instance, class);
return instance;
}
初始化流程如下表