vai alla Homepage di Apogeonline

 



Cos'è OpenPress
Glossario
Linux-FAQ
Documenti:

Open Source Definition

GNU General Public License

La cattedrale e il bazaar

Colonizzare la noosfera

Il calderone magico


Libri:

Italian crackdown

Open Sources

MediaMorfosi

GTK+/GNOME
sviluppo applicazioni


Telematica per la pace

Linux HOWTO: Installazione e configurazione

Linux HOWTO: Networking


Risorse
Feedback
vai alla Homepage di Apogeo Editore

Vai alla homepage di OpenPress

GTK+ / Gnome Sviluppo di Applicazioni


Finalizzazione di un oggetto

Per scrivere un GtkObject, è necessario implementare i metodi forniti dall'interfaccia di GtkObject, o al limite assicurarsi che le implementazioni fornite siano sufficienti. Esistono soltanto cinque metodi per GtkObject. Due di questi sono get_arg e set_arg, descritti nel la sezione Utilizzare gli argomenti degli oggetti per creare una propria sottoclasse di GtkObject. . Gli altri tre implementano la distruzione dell'oggetto. Ecco i membri della struttura GtkObjectClass:


  void (* shutdown) (GtkObject *object);
  void (* destroy)  (GtkObject *object);

  void (* finalize) (GtkObject *object);

Come potrete indovinare, gli oggetti vengono distrutti in un procedimento costituito da tre stadi. Ciascun metodo rappresenta uno stadio di questo processo. Se la la sottoclasse del vostro oggetto sostituisce una qualunque di queste, deve associarsi al metodo corrispondente nella classe superiore (). I tre metodi eseguono le seguenti operazioni:

  • Il metodo shutdown permette agli oggetti di eseguire determinate operazioni prima dell'inizio della propria distruzione. La maggior parte delle sottoclassi non sostituiscono questo metodo. Per default shutdown emette il segnale "destroy" per procedere allo stadio successivo. (L'implementazione standard viene sempre chiamata, anche se sostituita, poiché le sottoclassi devono associarsi alla classe superiore).

  • Il metodo destroy descrive l'oggetto come "inutile" e libera le risorse a questo associate, ma non l'oggetto stesso. Tipicamente un metodo destroy libera i dati, le stringhe e tutto ciò che è contenuto della struttura della istanza, ponendo i membri uguali a NULL. Questo è il metodo che la maggior parte dello sottoclassi sostituiscono.

  • Il metodo finalize viene chiamato unicamente quando il contatore di riferimento dell'oggetto diventa uguale a zero. L'implementazione di default libera la struttura dell'istanza dell'oggetto, quindi tutti i successivi tentativi di utilizzare l'oggetto causeranno un errore di segmentazione. Il metodo finalize deve inoltre tener conto che il codice dell'utente può essere stato chiamato dopo il metodo destroy, e liberare tutti i dati che questo codice aveva allocato.

Nota: Gli oggetti possono essere distrutti dipendentemente dal loro contatore di riferimento. Questo significa che il metodo shutdown viene chiamato e il segnale destroy emesso. Tuttavia, finché il contatore di riferimento non raggiunge un valore uguale a zero, l'oggetto non verrà finalizzato.

Il metodo shutdown non ha un ruolo definito. Il suo fine dipende dal tipo di oggetto. Ad esempio, l'implementazione di shutdown per GtkWidget rimuove il widget dal contenitore in cui si trova, e lo distrugge. Questo è molto importante per quanto riguarda i contenitori: il loro metodo destroy elimina tutti i widget che sia trovano al loro interno. Se il contenitorie non viene eliminato prima della sua distruzione, questo sarà ancora visibile e l'utente vedrà tutti i widget contenuti scomparire, seguiti dal contenitore. Attraverso il metodo shutdown, questi scompaiono tutti assieme.

Il metodo destroy libera tutte le risorse possibili senza rendere l'oggetto "insicuro". Se questo possiede delle invarianti che descrivono la propria integrità, un metodo destroy non le corromperà. Tutte le funzioni pubbliche esportate dall'implementazione dell'oggetto devono gestire con cura gli oggetti distrutti. Il metodo finalize attualmente libera gli oggetti, il che significa che ogni tentativo di accedere successivamente all'oggetto diventa un errore molto pericoloso.

La frase "le funzioni pubbliche esportate dall'implementazione dell'oggetto devono gestire con cura gli oggetti distrutti" richiede un minimo di spiegazione. Questo operazione viene intesa a priori, altrimenti il codice non può assicurarsi dell'integrita di un oggetto incrementando il contatore di riferimento. Tuttavia, l'implementazione non fornisce sempre questa garanzia. Alcune funzioni pubbliche di GTK+ e Gnome assumono che la liberazione delle strutture di dati avvenga nel metodo destroy, oppure la loro ri-allocazione nel caso siano già state liberate. A meno che il metodo finalize non liberi nuovamente queste strutture di dati, queste diventeranno un "memory leak". Per evitare questi errori, la soluzione migliore è quella di evitare di chiamare funzioni su oggetti ormai distrutti (in pratica, questo risulta molto raro).

È possibile far affidamento sul fatto che sarete comunque in grado di controllare il tipo e i flag di un oggetto distrutto. E certamente sarà sicuro chiamare gtk_object_unref() su un oggetto ormai distrutto. Nella implementazione del vostro oggetto, assicuratevi di implementare tutte le funzioni pubbliche correttamente. Controllate dove l'oggetto è distrutto utilizzando la macro GTK_OBJECT_DESTROYED(), e tenete bene a mente che il codice dell'utente può essere eseguito tra i metodi destroy e finalize.

Notare che il metodo destroy è il gestore default per il segnale "destroy", ma che i metodi shutdown e finalize sono unicamente funzioni di classe. Questo riduce la complessità e incrementa la velocità di distruzione. Inoltre, poiché finalize distrugge l'integrità di un oggetto, non è certo corretto farlo emettere come un segnale.

Per rendere le cose più concrete, esaminiamo adesso le funziono che dovreste utilizzare per distruggere un oggetto. Per prima, 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);
    }
}

Notate che gli oggetti distrutti ma non finalizzati posseggono un flag, il quale può essere esaminato tramite la macro GTK_OBJECT_DESTROYED(). gtk_object_destroy() assicura che gli oggetti non vengono distrutti due volte consecutive, semplicemente ignorando quelli già distrutti. Se un oggetto non lo è ancora stato, gtk_object_destroy() fa riferimento a questo per prevenire la finalizzazione durante il processo di distruzione e chiama il metodo di shutdown. Per default, il metodo è implementato come segue:


static void
gtk_object_shutdown (GtkObject *object)
{
  GTK_OBJECT_SET_FLAGS (object, GTK_DESTROYED);
  gtk_signal_emit (object, object_signals[DESTROY]);
}

Questo metodo imposta il flag di distruzione, per assicurarsi che qualunque chiamata ricorsiva a gtk_object_destroy() non abbia effetto. Successivamente, questo emette il segnale "destroy". gtk_object_shutdown() sembra inutile fuori da questo contesto, tuttavia le sottoclassi possono sostituire questo metodo con qualcosa di più sostanzioso, associandandolo al metodo default di GtkObject ( la sezione Associazioni ).

Può non essere chiaro perché gtk_object_shutdown() è una implementazione di un metodo, mentre gtk_object_destroy() è una funziona pubblica. Notare che gtk_object_shutdown() è una funzione interna che implementa il metodo shutdown per la classe GtkObject, mentre gtk_object_destroy() è parte della API pubblica. L'implementazione per GtkObject del metodo destroy è chiamata gtk_object_real_destroy():


static void
gtk_object_real_destroy (GtkObject *object)
{
  if (GTK_OBJECT_CONNECTED (object))
    gtk_signal_handlers_destroy (object);
}

Questa parte di codice elimina molto semplicemente tutti i gestori associati all'oggetto. gtk_object_real_destroy() è il gestore default chiamato quando il segnale "destroy" viene emesso. gtk_object_destroy() chiama la funzione di classe shutdown. Il metodo default per shutdown emette il segnale "destroy".

Il processo di finalizzazione inizia con la chiamata a gtk_object_unref(), se e solo se il contatore di riferimento ha raggiunto un valore uguale a zero. gtk_object_unref() può essere chiamata direttamente dall'utente, ma lo è anche da gtk_object_destroy(). Ecco la funzione:


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);
    }
}

Se un oggetto possiede il valore del contatore di riferimento uguale a 1, la chiamata a gtk_object_unref() chiama i metodi shutdown e destroy (attraverso gtk_object_destroy()) e quindi finalizza l'oggetto (a meno che il contatore non venga incrementato durante il processo di shutdown/destroy. Questo è permesso e permette di evitare la finalizzazione). Se il contatore di un oggetto è maggiore di 1 al momento della chiamata a gtk_object_unref(), questo viene semplicemente decrementato.

Di nuovo, possiamo notare che un oggetto può essere distrutto mentre il contatore ha un valore maggiore di 1 se l'utente chiama la funzione gtk_object_destroy(). Se questo accade, la finalizzazione non ha luogo fino a che il contatore non raggiunge 0 attraverso gtk_object_unref(). Nei casi più comuni, la funzione gtk_object_destroy() agisce sull'ultimo valore del contatore (guardate nuovamente il codice di gtk_object_destroy() tenendo a mente questa affermazione).

Per completare la sequenza di esempi, ecco l'implementazione del metodo finalize per GtkObject:


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);
}

Le tre funzioni chiamate in questo metodo eseguono le seguenti operazioni:

  • Chiamano le callback per la finalizzazione dell'oggetto. Questa è una delle caratteristiche meno utilizzate di GtkObject e non viene descritta in questo libro (solitamente, viene associata al segnale "destroy").

  • Elimina tutti i dati di un oggetto (funzione descritta nel la sezione Associare dati agli oggetti ).

  • Libera la struttura dell'istanza dell'oggetto.

la sezione Il ciclo vitale di un widget nel il capitolo Fondamenti di GTK+ fornisce maggiori informazioni riguardo i contatori di riferimento e la distruzione dei widgets.

Associazioni

Se un oggetto sostituisce i metodi shutdown, destroy oppure finalize, deve associare questi alla implementazione di default, per assicurarsi che tutti le classi superiori abbiamo la possibilità di distruggersi. Ecco un esempio:


static void
gtk_widget_real_destroy (GtkObject *object)
{

  /* ... */

  if (parent_class->destroy)
    parent_class->destroy (object);

};

gtk_widget_real_destroy() è inserita nella struttura della classe del widget durante l'inizializzazione di quest'ultima, sovrascrivendo il default per GtkObject. parent_class è un puntatore alla struttura della classe superiore. Solitamente si gestisce questo puntatore nella funzione che inizializza la classe, come in GtkWidget:


static GtkObjectClass *parent_class = NULL;

/* ... omissione di codice ... */

static void
gtk_widget_class_init (GtkWidgetClass *klass)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) klass;

  parent_class = gtk_type_class (gtk_object_get_type ());

  /* ... omissione di codice ... */

  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;
} 

Ovviamente, se parent_class non è una GtkObjectClass*, dovrete eseguire un cast su questa utilizzando la macro GTK_OBJECT_CLASS().

Una piccola parentesi: notare che non dovete associarvi alla classe superiori durante l'implementazione di get_arg e set_arg. GTK+ tratta in modo molto particolare questi metodi attraverso gtk_object_set() e gtk_object_get(). Ricordatevi che l'inizializzatore della classe di base per GtkObject azzera questi due metodi, piuttosto che lasciare l'implementazione di default. Quando impostate o leggete i valori degli argomenti, GTK+ utilizza le informazioni fornitegli durante la registrazione di questi per saltare direttamente alla struttura della classe appropriata e per chiamare i metodi get_arg e set_arg corretti. Associarsi alla classe superiore implicherebbe un notevole calo nelle prestazioni, per ottenere lo stesso identico risultato.


Copyright © 1995-1999 Apogeo srl, Milano