The GObject base class
The previous chapter discussed the details of GLib's Dynamic Type System.
The GObject library also contains an implementation for a base fundamental
type named GObject.
GObject is a fundamental classed instantiable type. It implements:
Memory management with reference countingConstruction/Destruction of instancesGeneric per-object properties with set/get function pairsEasy use of signals
All the GNOME libraries which use the GLib type system (like GTK+ and GStreamer)
inherit from GObject which is why it is important to understand
the details of how it works.
Object instantiation
The g_object_new
family of functions can be used to instantiate any GType which inherits
from the GObject base type. All these functions make sure the class and
instance structures have been correctly initialized by GLib's type system
and then invoke at one point or another the constructor class method
which is used to:
Allocate and clear memory through g_type_create_instance,
Initialize the object's instance with the construction properties.
Although one can expect all class and instance members (except the fields
pointing to the parents) to be set to zero, some consider it good practice
to explicitly set them.
Objects which inherit from GObject are allowed to override this
constructor class method: they should however chain to their parent
constructor method before doing so:
GObject *(* constructor) (GType gtype,
guint n_properties,
GObjectConstructParam *properties);
The example below shows how MamanBar overrides the parent's constructor:
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#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;
/* instance members */
};
struct _MamanBarClass
{
GObjectClass parent_class;
/* class members */
};
/* will create maman_bar_get_type and set maman_bar_parent_class */
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
static GObject *
maman_bar_constructor (GType gtype,
guint n_properties,
GObjectConstructParam *properties)
{
GObject *obj;
{
/* Always chain up to the parent constructor */
obj = G_OBJECT_CLASS (maman_bar_parent_class)->constructor (gtype, n_properties, properties);
}
/* update the object state depending on constructor properties */
return obj;
}
static void
maman_bar_class_init (MamanBarClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = maman_bar_constructor;
}
static void
maman_bar_init (MamanBar *self)
{
/* initialize the object */
}
If the user instantiates an object MamanBar with:
MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);
If this is the first instantiation of such an object, the
maman_bar_class_init function will be invoked
after any maman_bar_base_class_init function.
This will make sure the class structure of this new object is
correctly initialized. Here, maman_bar_class_init
is expected to override the object's class methods and setup the
class' own methods. In the example above, the constructor method is
the only overridden method: it is set to
maman_bar_constructor.
Once g_object_new has obtained a reference to an initialized
class structure, it invokes its constructor method to create an instance of the new
object. Since it has just been overridden by maman_bar_class_init
to maman_bar_constructor, the latter is called and, because it
was implemented correctly, it chains up to its parent's constructor. In
order to find the parent class and chain up to the parent class
constructor, we can use the maman_bar_parent_class
pointer that has been set up for us by the
G_DEFINE_TYPE macro.
Finally, at one point or another, g_object_constructor is invoked
by the last constructor in the chain. This function allocates the object's instance' buffer
through g_type_create_instance
which means that the instance_init function is invoked at this point if one
was registered. After instance_init returns, the object is fully initialized and should be
ready to answer any user-request. When g_type_create_instance
returns, g_object_constructor sets the construction properties
(i.e. the properties which were given to g_object_new) and returns
to the user's constructor which is then allowed to do useful instance initialization...
The process described above might seem a bit complicated, but it can be
summarized easily by the table below which lists the functions invoked
by g_object_new
and their order of invocation:
g_object_newInvocation timeFunction InvokedFunction's parametersRemarkFirst call to g_object_new for target typetarget type's base_init functionOn the inheritance tree of classes from fundamental type to target type.
base_init is invoked once for each class structure.
I have no real idea on how this can be used. If you have a good real-life
example of how a class' base_init can be used, please, let me know.
target type's class_init functionOn target type's class structure
Here, you should make sure to initialize or override class methods (that is,
assign to each class' method its function pointer) and create the signals and
the properties associated to your object.
interface' base_init functionOn interface' vtableinterface' interface_init functionOn interface' vtableEach call to g_object_new for target typetarget type's class constructor method: GObjectClass->constructorOn object's instance
If you need to complete the object initialization after all the construction properties
are set, override the constructor method and make sure to chain up to the object's
parent class before doing your own initialization.
In doubt, do not override the constructor method.
type's instance_init functionOn the inheritance tree of classes from fundamental type to target type.
the instance_init provided for each type is invoked once for each instance
structure.
Provide an instance_init function to initialize your object before its construction
properties are set. This is the preferred way to initialize a GObject instance.
This function is equivalent to C++ constructors.
Readers should feel concerned about one little twist in the order in
which functions are invoked: while, technically, the class' constructor
method is called before the GType's instance_init
function (since g_type_create_instance which calls instance_init is called by
g_object_constructor which is the top-level class
constructor method and to which users are expected to chain to), the
user's code which runs in a user-provided constructor will always
run after GType's instance_init function since the
user-provided constructor must (you've been warned)
chain up before doing anything useful.
Object memory management
The memory-management API for GObjects is a bit complicated but the idea behind it
is pretty simple: the goal is to provide a flexible model based on reference counting
which can be integrated in applications which use or require different memory management
models (such as garbage collection). The methods which are used to
manipulate this reference count are described below.
/*
Refcounting
*/
gpointer g_object_ref (gpointer object);
void g_object_unref (gpointer object);
/*
* Weak References
*/
typedef void (*GWeakNotify) (gpointer data,
GObject *where_the_object_was);
void g_object_weak_ref (GObject *object,
GWeakNotify notify,
gpointer data);
void g_object_weak_unref (GObject *object,
GWeakNotify notify,
gpointer data);
void g_object_add_weak_pointer (GObject *object,
gpointer *weak_pointer_location);
void g_object_remove_weak_pointer (GObject *object,
gpointer *weak_pointer_location);
/*
* Cycle handling
*/
void g_object_run_dispose (GObject *object);
Reference count
The functions g_object_ref/g_object_unref respectively
increase and decrease the reference count.These functions are thread-safe as of GLib 2.8.
The reference count is, unsurprisingly, initialized to one by
g_object_new which means that the caller
is currently the sole owner of the newly-created reference.
When the reference count reaches zero, that is,
when g_object_unref is called by the last client holding
a reference to the object, the dispose and the
finalize class methods are invoked.
Finally, after finalize is invoked,
g_type_free_instance is called to free the object instance.
Depending on the memory allocation policy decided when the type was registered (through
one of the g_type_register_* functions), the object's instance
memory will be freed or returned to the object pool for this type.
Once the object has been freed, if it was the last instance of the type, the type's class
will be destroyed as described in and
.
The table below summarizes the destruction process of a GObject:
g_object_unrefInvocation timeFunction InvokedFunction's parametersRemarkLast call to g_object_unref for an instance
of target type
target type's dispose class functionGObject instance
When dispose ends, the object should not hold any reference to any other
member object. The object is also expected to be able to answer client
method invocations (with possibly an error code but no memory violation)
until finalize is executed. dispose can be executed more than once.
dispose should chain up to its parent implementation just before returning
to the caller.
target type's finalize class functionGObject instance
Finalize is expected to complete the destruction process initiated by
dispose. It should complete the object's destruction. finalize will be
executed only once.
finalize should chain up to its parent implementation just before returning
to the caller.
The reason why the destruction process is split is two different phases is
explained in .
Last call to g_object_unref for the last
instance of target type
interface' interface_finalize functionOn interface' vtableNever used in practice. Unlikely you will need it.interface' base_finalize functionOn interface' vtableNever used in practice. Unlikely you will need it.target type's class_finalize functionOn target type's class structureNever used in practice. Unlikely you will need it.type's base_finalize functionOn the inheritance tree of classes from fundamental type to target type.
base_init is invoked once for each class structure.Never used in practice. Unlikely you will need it.
Weak References
Weak References are used to monitor object finalization:
g_object_weak_ref adds a monitoring callback which does
not hold a reference to the object but which is invoked when the object runs
its dispose method. As such, each weak ref can be invoked more than once upon
object finalization (since dispose can run more than once during object
finalization).
g_object_weak_unref can be used to remove a monitoring
callback from the object.
Weak References are also used to implement g_object_add_weak_pointer
and g_object_remove_weak_pointer. These functions add a weak reference
to the object they are applied to which makes sure to nullify the pointer given by the user
when object is finalized.
Reference counts and cycles
Note: the following section was inspired by James Henstridge. I guess this means that
all praise and all curses will be directly forwarded to him.
GObject's memory management model was designed to be easily integrated in existing code
using garbage collection. This is why the destruction process is split in two phases:
the first phase, executed in the dispose handler is supposed to release all references
to other member objects. The second phase, executed by the finalize handler is supposed
to complete the object's destruction process. Object methods should be able to run
without program error (that is, without segfault :) in-between the two phases.
This two-step destruction process is very useful to break reference counting cycles.
While the detection of the cycles is up to the external code, once the cycles have been
detected, the external code can invoke g_object_run_dispose which
will indeed break any existing cycles since it will run the dispose handler associated
to the object and thus release all references to other objects.
Attentive readers might now have understood one of the rules about the dispose handler
we stated a bit sooner: the dispose handler can be invoked multiple times. Let's say we
have a reference count cycle: object A references B which itself references object A.
Let's say we have detected the cycle and we want to destroy the two objects. One way to
do this would be to invoke g_object_run_dispose on one of the
objects.
If object A releases all its references to all objects, this means it releases its
reference to object B. If object B was not owned by anyone else, this is its last
reference count which means this last unref runs B's dispose handler which, in turn,
releases B's reference on object A. If this is A's last reference count, this last
unref runs A's dispose handler which is running for the second time before
A's finalize handler is invoked !
The above example, which might seem a bit contrived can really happen if your
GObject's are being handled by language bindings. I would thus suggest the rules stated above
for object destruction are closely followed. Otherwise, Bad Bad Things
will happen.
Object properties
One of GObject's nice features is its generic get/set mechanism for object
properties. When an object
is instantiated, the object's class_init handler should be used to register
the object's properties with the G_DEFINE_PROPERTIES
macro.
The best way to understand how object properties work is by looking at a real example
on how it is used:
typedef struct {
char *name;
gint age;
} PersonPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (Person, person, G_TYPE_OBJECT)
static void
person_finalize (GObject *gobject)
{
PersonPrivate *priv = person_get_instance_private ((Person *) gobject);
g_free (priv->name);
G_OBJECT_CLASS (person_parent_class)->finalize (gobject);
}
static void
person_class_init (PersonClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = person_finalize;
G_DEFINE_PROPERTIES (Person, person, klass,
G_DEFINE_PROPERTY (Person, string, name, G_PROPERTY_READWRITE | G_PROPERTY_COPY_SET)
G_DEFINE_PROPERTY (Person, uint, age, G_PROPERTY_READWRITE))
}
The code above will add two properties, name and
age to the Person class;
the name property is a string, and the age property is a generic unsigned
integer. Both properties are readable and writable. Additionally, the name
property is implemented by copying the string passed to the setter, but
the get function will return a pointer. By default, the properties will
be stored inside the private data structure on a Person instance, using
the given field name.
Once we define the properties, their types, their access semantics,
and their visibility, we can access them through the generic GObject
API:
Person *person;
person = g_object_new (person_get_type (), NULL);
g_object_set (person,
"name", "Rupert S. Monkey",
"age", 33,
NULL);
The code above will set the two properties on the person
instance.
It should be noted that using the generic GObject API should not be the
common pattern for setting properties in C. The generic API uses variadic
arguments, which have side effects in terms of error handling, type
inference, and argument termination; those side effects can lead to
hard to track bugs. One option to avoid the variadic arguments API is to
use the GValue-based one, e.g.
GValue name_value = G_VALUE_INIT;
GValue age_value = G_VALUE_INIT;
g_value_init (&name_value, G_TYPE_STRING);
g_value_set_string (&name_value, "Rupert S. Monkey");
g_value_init (&age_value, G_TYPE_INT);
g_value_set_int (&age_value, 33);
g_object_set_property (G_OBJECT (person), "name", &name_value);
g_object_set_property (G_OBJECT (person), "age", &age_value);
g_value_unset (&name_value);
g_value_unset (&age_value);
But, as you can see from the example above, the amount of code necessary
balloons rapidly out of control.
The preferred way to access properties on a GObject is to use accessor
functions, which guarantee type safety when compiling code, and avoid
large amounts of code. For instance:
void
person_set_name (Person *person, const char *name)
{
GProperty *property;
property = g_object_class_find_property (G_OBJECT_GET_CLASS (person), "name");
g_property_set (property, person, name);
}
const char *
person_get_name (Person *person)
{
PersonPrivate *priv = person_get_instance_private (person);
return priv->name;
}
GObject provides pre-processor macros to simplify the developer's job at
creating these functions, which result in cleaner and simpler code; for
instance, the example above can be effectively replaced by a single
macro:
G_DEFINE_PROPERTY_GET_SET (Person, person, const char *, name)
The macro above will generate both the get function and the set one.
Property notification and binding
Every time a property is set to a new value, GObject will emit a signal
called notify; this signal can be used to update
ancillary code every time the state of an instance changes; for instance,
it is possible to update the text field of a GUI with the content of the
person's name, if the Person instance is changed by another part of the
GUI, or by another source.
The notification signal is detailed with the name of the property which
has changed, so it's possible to subscribe to changes to specific
properties, for instance:
static void
on_name_change (GObject *gobject,
GParamSpec *pspec,
gpointer user_data)
{
Person *person = (Person *) gobject;
GtkLabel *label = user_data;
gtk_label_set_text (label, person_get_name (person));
}
...
g_signal_connect (person, "notify::name", G_CALLBACK (on_name_change), ui_label);
...
The code above will update the contents of a GtkLabel using the name
property every time the property changes.
It is also possible to use the property bindings to tie together a
property on a source instance and a property on a target instance.
In the example above, we could bind together the name property on a
Person instance to the text property on a GtkLabel instance:
g_object_bind_property (person, "name", label, "text", G_BINDING_DEFAULT);
Thus removing the need to have an explicit function doing so.