Seitenleiste

Resourcenfreigabe im Destruktor

Im letzten Kapitel lernten wir die *_init Methoden kennen, in denen ihr Speicher allokieren und Objekte erstellen könnt. Irgendwann wird jedoch eine Instanz einer Klasse nicht mehr benötigt, sie wird freigegeben. Dabei sollte, um Speicherlecks zu vermeiden, jedes Stück Speicher, das die Klasse alloziert hat, wieder freigeben werden.

dispose - Referenzen abgeben

GObject unterstützt Reference counting. Erstellt eine Klasse ein Objekt, und übergibt eine Referenz dieses Objektes an eine andere Klasse, so wird mit g_object_ref der interne Zähler für Referenzen um 1 hochgesetzt.

Wird nun eine Klasse zerstört, die eine Referenz dieses Objektes besitzt, so muss der Referenzenzähler um 1 zurückhgesetzt werden (g_object_unref). Ist der Zähler dann 0, gibt GLib das Objekt selbstständig frei.

tutorial-tier.c
static void
tutorial_tier_dispose(GObject *gobject)
{
    TutorialTier *self = TUTORIAL_TIER(gobject);
 
    /* hier können Referenzen abgegeben werden */
 
    G_OBJECT_CLASS(tutorial_tier_parent_class)->dispose(gobject);
}

Mit der letzten Zeile springen wir in die Basisklasse und rufen die dortige dispose Funktion auf.

Habt ihr nun in tutorial_tier_init eine Referenz auf ein anderes GObject aufgebaut (sprich: ihr habt mit g_object_new eines erstellt), gibt ihr diese wieder ab mit folgendem Code:

if (self->priv->meine_referenz)
{
    g_object_unref(self->priv->meine_referenz);
    self->priv->meine_referenz = NULL;
}

Ihr werdet euch sicher fragen, was der if-Block hier soll. Nun: Bei der Zerstörung eines GObjects kann dispose mehrfach aufgerufen werden (z.Bsp. wenn verschiedene Klassen von dieser abgeleitet sind). Der if-Block dient dazu, einen mehrfachen Aufruf von g_object_unref zu verhindern.

finalize - Speicher freigeben

Während in dispose Referenzen auf andere GObjects freigeben wurden, muss in finalize jeder Speicher freigeben werden, der mit g_malloc (oder eben malloc) alloziert wurde.

Bei finalize könnt ihr euch Sicher sein, dass es nur einmal aufgerufen wird.

tutorial-tier.c
static void
tutorial_tier_finalize(GObject *gobject)
{
    TutorialTier *self = TUTORIAL_TIER(gobject);
 
    g_free(self->priv->ruf);
 
    G_OBJECT_CLASS(tutorial_tier_parent_class)->finalize(gobject);
}

An Ende springen wir wieder in die finalize-Methode der Basisklasse.

Registrierung der Funktionen

finalize und dispose sind beides virtuelle Funktionen der GObject-Klasse, die wir nun überschrieben haben. Jedoch müssen wir dies GObject noch mitteilen.

Dies wird in der tutorial_tier_class_init Methode erledigt:

 tutorial-tier.c 
static void
tutorial_tier_class_init(TutorialTierClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
 
    gobject_class->dispose = tutorial_tier_dispose;
    gobject_class->finalize = tutorial_tier_finalize;
 
    g_type_class_add_private(klass, sizeof(TutorialTierPrivate));
}

Zwischenstand

Unsere Klasse ist jetzt zum ersten Mal vollständig einsatzbereit. Zur besseren Übersicht nun die komplette C-Datei:

tutorial-tier.c
/*
 * Copyright- und Lizenzinformationen
 */
 
#include "tutorial-tier.h"
 
static struct _TutorialTierPrivate
{
    gint alter;
    gchar *ruf;
};
 
#define TUTORIAL_TIER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TUTORIAL_TYPE_TIER, TutorialTierPrivate))
 
G_DEFINE_TYPE(TutorialTier, tutorial_tier, G_TYPE_OBJECT);
 
 
static void
tutorial_tier_dispose(GObject *gobject)
{
    TutorialTier *self = TUTORIAL_TIER(gobject);
 
    /* hier können Referenzen abgegeben werden */
 
    G_OBJECT_CLASS(tutorial_tier_parent_class)->dispose(gobject);
}
 
static void
tutorial_tier_finalize(GObject *gobject)
{
    TutorialTier *self = TUTORIAL_TIER(gobject);
 
    g_free(self->priv->ruf);
 
    G_OBJECT_CLASS(tutorial_tier_parent_class)->finalize(gobject);
}
 
 
static void
tutorial_tier_class_init(TutorialTierClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
 
    gobject_class->dispose = tutorial_tier_dispose;
    gobject_class->finalize = tutorial_tier_finalize;
 
    g_type_class_add_private(klass, sizeof(TutorialTierPrivate));
}
 
static void
tutorial_tier_init(TutorialTier *self)
{
    self->priv = TUTORIAL_TIER_GET_PRIVATE(self);
 
    self->priv->ruf = g_malloc(10);
}