To write a GtkObject, you
must implement the methods provided by the GtkObject interface, or at least be
sure you are happy with the default implementations. There
are only five GtkObject
methods; two of them are
get_arg and set_arg,
described in the
section called Using Object Arguments in Your Own GtkObject Subclass. The
other three implement object destruction; here are the
fields in GtkObjectClass:
void (* shutdown) (GtkObject *object);
void (* destroy) (GtkObject *object);
void (* finalize) (GtkObject *object);
|
As you might guess from this, objects are destroyed in a
three-stage process. Each method represents one stage in
the process; if your object subclass overrides any of them,
it must "chain up" to the corresponding method in the
parent class (see the section called
Chaining Up). The three methods do the following:
-
The shutdown method
allows objects to perform actions before destruction
begins. Most subclasses do not override this method;
the default shutdown
method emits the "destroy"
signal to start the next phase. (The default
implementation will always be
invoked, even if overridden, because subclasses are
required to "chain up.")
-
The destroy method
marks the object "useless" and cleans up associated
resources, but does not free the object itself.
Typically a destroy method would free data, strings,
and so on stored in the instance struct, and set the
struct members to NULL.
This is the method most subclasses override.
-
The finalize method is
invoked only when the object's
reference count reaches 0. The default
implementation frees the object instance struct, so
that further attempts to use the object result in a
segmentation fault. The
finalize method must also consider that user
code could have been invoked after the destroy method, and free any data
that user code could have allocated.
Note: Objects can be destroyed
regardless of their reference count. This means that
the shutdown method is
invoked and the destroy
signal is emitted. However, as long as the reference count
is greater than 0, the object will not be finalized.
The shutdown method has no
defined role; its purpose depends on the particular object.
For example, the GtkWidget
shutdown implementation removes the widget from its parent
container, and unrealizes the widget. This is especially
important for containers: their
destroy method destroys all children of the
container. If the container was not unrealized before
destruction, it would still be visible and the user would
see each child disappear, followed by the container. With
the shutdown method,
everything disappears at once.
The destroy method frees as
many resources as possible without rendering the object
"unsafe." If your object has invariants describing its
integrity, a destroy method
will not violate them. All public functions exported by an
object implementation should gracefully handle destroyed
objects (they should not crash---remember that an object
can be destroyed while references to it persist). The finalize method actually frees
the object, meaning that attempts to use the object become
dangerous bugs.
The statement that "public functions exported by an object
implementation should gracefully handle destroyed objects"
requires some qualification. This is the intended behavior;
otherwise, code could not ensure the sanity of an object by
increasing its reference count. However, the implementation
does not yet live up to the guarantee in all cases. Some
public functions in GTK+ and Gnome still assume data
structures freed in the destroy method exist, or
re-allocate data structures the destroy method already
freed. Unless the finalize method re-frees those data
structures, they will be leaked. To avoid these bugs, it is
best to avoid calling functions on destroyed objects (in
practice, it would be uncommon to do so).
You can count on being able to
check the type and object flags of a destroyed object,
however; and it is certainly safe to call gtk_object_unref() on a destroyed object.
In your own object implementations, be sure you implement
each public function correctly; check whether the object is
destroyed with
GTK_OBJECT_DESTROYED(), and keep in mind that user
code can run between the
destroy method and the
finalize method.
Notice that the destroy
method is the default handler for a
"destroy" signal, but the
shutdown and
finalize methods are class functions only. This
reduces the complexity and increases the speed of the
finalization process. Also, because finalize destroys the integrity of an
object, it would be unsafe to emit as a signal (GTK+ does
have a facility called "weak references" that allows you to
invoke a callback when an object is finalized---weak
references do not assume that the
GtkObject is in a sane state).
To make things more concrete, let's look at the functions
you would use to destroy an object. First, gtk_object_destroy():
void
gtk_object_destroy (GtkObject *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_OBJECT (object));
g_return_if_fail (GTK_OBJECT_CONSTRUCTED (object));
if (!GTK_OBJECT_DESTROYED (object))
{
gtk_object_ref (object);
object->klass->shutdown (object);
gtk_object_unref (object);
}
}
|
Notice that destroyed-but-not-finalized objects are
flagged, and this flag can be checked with the GTK_OBJECT_DESTROYED() macro. gtk_object_destroy() ensures that objects
are not destroyed twice by ignoring any already-destroyed
objects. If an object has not been destroyed, gtk_object_destroy() references it to
prevent finalization during the destroy process and invokes
the shutdown method; by default, that method looks like
this:
static void
gtk_object_shutdown (GtkObject *object)
{
GTK_OBJECT_SET_FLAGS (object, GTK_DESTROYED);
gtk_signal_emit (object, object_signals[DESTROY]);
}
|
This method sets the destroyed flag, to ensure that any
recursive gtk_object_destroy()
calls have no effect; then it emits the "destroy" signal.
gtk_object_shutdown() seems pointless by itself;
however, subclasses may override this method with something
more substantial, chaining up to the GtkObject default method (see the section called
Chaining Up).
It may be unclear that
gtk_object_shutdown() is a method implementation,
while gtk_object_destroy() is a
public function. Note that
gtk_object_shutdown() is the internal function that
implements the shutdown
method for the GtkObject
class, while gtk_object_destroy()
is part of the public API. The
GtkObject implementation of the destroy method is called gtk_object_real_destroy():
static void
gtk_object_real_destroy (GtkObject *object)
{
if (GTK_OBJECT_CONNECTED (object))
gtk_signal_handlers_destroy (object);
}
|
This code simply cleans up any signal handlers associated
with the object.
gtk_object_real_destroy() is the default handler
invoked when the "destroy"
signal is emitted.
gtk_object_destroy() invokes the (possibly overridden)
class function shutdown;
the default shutdown method emits the
"destroy" signal.
Finalization is initiated by
gtk_object_unref(), if and only if the reference count
has reached 0. gtk_object_unref()
can be invoked directly by a user, but often gtk_object_destroy() invokes it. Here it
is:
void
gtk_object_unref (GtkObject *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_OBJECT (object));
g_return_if_fail (object->ref_count > 0);
if (object->ref_count == 1)
{
gtk_object_destroy (object);
g_return_if_fail (object->ref_count > 0);
}
object->ref_count -= 1;
if (object->ref_count == 0)
{
object->klass->finalize (object);
}
}
|
If an object has a reference count of 1, calling gtk_object_unref() invokes the shutdown and
destroy methods (via
gtk_object_destroy()) and then finalizes the object
(unless the reference count was incremented sometime during
the shutdown/destroy process; this is allowed and will
prevent finalization). If an object's reference count is
greater than 1 at the start of
gtk_object_unref(), the reference count is simply
decremented.
Again, notice that an object can be
destroyed while the reference count is greater than 1
if the user calls
gtk_object_destroy(); if this happens, finalization
does not take place until the holders of the remaining
references call
gtk_object_unref(). In the most common case, the gtk_object_destroy() implementation
holds the last reference count --- have another look at the
gtk_object_destroy() code with
this in mind.
For completeness, here is
GtkObject's default
finalize method:
static void
gtk_object_finalize (GtkObject *object)
{
gtk_object_notify_weaks (object);
g_datalist_clear (&object->object_data);
gtk_type_free (GTK_OBJECT_TYPE (object), object);
}
|
The three function calls in this method do the following:
-
Invoke "weak references," which are callbacks invoked
on object finalization. This is a little-used GtkObject feature not
described in this book (usually connecting to the "destroy" signal is more
appropriate).
-
Clear any object data (described in the section called Attaching
Data to Objects).
-
Free the instance struct.
the section called
Widget Life Cycle in the chapter called GTK+
Basics has more to say about reference counting and
destruction with respect to widgets.
If an object overrides the shutdown, destroy, or finalize
methods, it should chain up to the default
implementation, to ensure that each parent class has a
chance to clean up. Here is an example of chaining up:
static void
gtk_widget_real_destroy (GtkObject *object)
{
/* ... */
if (parent_class->destroy)
parent_class->destroy (object);
};
|
gtk_widget_real_destroy() is
installed in the widget's class struct in the class
initialization function, overwriting the GtkObject default. parent_class is a pointer to the
parent's class struct; usually you will want to store
this pointer in your class initialization function, as
GtkWidget does:
static GtkObjectClass *parent_class = NULL;
/* ... code omitted ... */
static void
gtk_widget_class_init (GtkWidgetClass *klass)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass*) klass;
parent_class = gtk_type_class (gtk_object_get_type ());
/* ... code omitted ... */
object_class->set_arg = gtk_widget_set_arg;
object_class->get_arg = gtk_widget_get_arg;
object_class->shutdown = gtk_widget_shutdown;
object_class->destroy = gtk_widget_real_destroy;
object_class->finalize = gtk_widget_finalize;
}
|
Of course, if
parent_class is not a
GtkObjectClass*, you will need to cast it with the
GTK_OBJECT_CLASS() macro.
An aside: notice that you should
not chain up when implementing get_arg and
set_arg --- GTK+ special-cases these methods in
gtk_object_set() and gtk_object_get(). Recall that the GtkObject base class
initializer zeroes these two methods, rather than leaving
the default implementation. When setting or getting an
argument value, GTK+ uses the information provided on
argument registration to jump directly to the correct
class struct and invoke only the correct get_arg or
set_arg method. Chaining up would be a much slower
way to implement the same thing (and would require unique
argument IDs within the same class ancestry).