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方法。
类的构造方法的作用如下:
- 通过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){
......}elseobject = 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_COLORDg_object_unref (backend_cups->colord_client);
#endifg_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;
}
初始化流程如下表