RSS
 

Distruttori di classe #2

19 Jun

Nell’articolo precedente abbiamo visto come creare un metodo che permetta di rilasciare le risorse al termine dell’utilizzo. Non sempre però è possibile determinare il momento del rilascio, ad esempio quando è legato ad eventi esterni. In questo caso è necessario che il distruttore sia eseguito proprio quando l’oggetto sia in fase di smaltimento, in automatico.

Con un linguaggio che adopera un garbage collector però bisogna fare molta attenzione per il semplice motivo che non si sa quando verrà eseguita la pulizia e solo in questo momento viene scaturito l’evento che richiama il metodo distruttore.

Quando distruggiamo esplicitamente un oggetto da codice, valorizzandolo con nil, stiamo solamente indicando l’intenzione, sarà poi compito del garbage collector analizzare se effettivamente non esiste nessun riferimento a quell’area di memoria, basta un solo indirizzamento anche involontario (i casi più probabili) per impedirne il rilascio. Poi c’è l’incognita di quando tutto ciò verrà fatto.

Riprendiamo l’esempio precedente con qualche aggiunta al codice:

class E
  #Creo una variabile di istanza, utile per i test
  attr_reader :var1
 
  #Costruttore
  def initialize(var1)
    puts "Initializing #{self.object_id} var #{var1}"
    @var1=var1
  end
 
  #Metodo di servizio
  def body
    begin
      yield
    rescue StandardError => err
      puts "Error: #{err}"
    ensure
      finalize
    end
  end
 
  #Metodo che fa il lavoro sporco
  def work
    puts 'Work'
  end
 
  private
  #Distruttore
  def finalize
    puts "End #{@var1}"
  end
 
end
 
#Istanzio il primo oggetto
e1=E.new('Try1')
e1.body do
  e1.work
end
 
#Istanzio il secondo oggetto per il quale ho anche la necessità
#di rilasciare alcune risorse in un momento indeterminato
e2=E.new('Try2')
e2.body do
  ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"})
  e2.work
end
 
puts 'Oggetti prima della pulizia:'
ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}
 
#Distruggo gli oggetti
e1=nil
e2=nil
 
puts 'Richiamo il garbage collector per la pulizia...'
GC.start
sleep(2)
puts 'Oggetti dopo la pulizia:'
ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}

Se eseguiamo questo script otterremo il seguente output:

D:\Marco\Progetti\Ruby\Batch\Test>ruby D2.rb
Initializing 20805980 var Try1
Work
End Try1
Initializing 20805910 var Try2
Work
End Try2
Oggetti prima della pulizia:
Oggetto: 20805910
Oggetto: 20805980
Richiamo il garbage collector per la pulizia...
Finalizing 20805910
Oggetti dopo la pulizia:

La definizione della classe è rimasta identica, questa volta stanzio due oggetti, il primo è identico all’articolo precedente, per il secondo invece ho ipotizzato che dovesse gestire il rilascio di alcune risorse in un momento x, noi non sappiamo quando si verificherà sappiamo solo che quando avverrà dobbiamo fare in modo che vengano deallocate alcune risorse.

Un esempio concreto è quando si scrive un file mediante un processo esterno, dobbiamo riferire di chiuderlo prima di terminare la nostra applicazione, anche se si verifica qualcosa di inatteso.

Se esaminiamo il secondo oggetto:

e2=E.new('Try2')
e2.body do
  ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"})
  e2.work
end

Si nota che abbiamo fatto uso della classe ObjectSpace, contenitore di tutte le classi stanziate, per associare la dimissione dell’oggetto e2 alla proc passata come parametro. Tradotto in parole semplici stiamo dicendo: prima di distruggere l’oggetto esegui quella procedura.

Se riguardiamo l’output si può notare un altro l’utilizzo della classe ObjectSpace:

ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}

E’ possibile esaminare ogni oggetto istanziato e specificando come parametro la classe E possiamo filtrare solo quella che ci interessa (attenzione che senza filtro la lista potrebbe essere molto lunga).

Tornando all’output: gli oggetti trovati nel contenitore sono due e viene mostrato l’id di riferimento. Dopo di che vengono distrutti e viene forzata l’esecuzione del garbage collector con GC.start. In alternativa a questo è anche possibile eseguire il metodo garbage_collect di ObjectSpace

Dopo la pulizia viene generato l’evento che richiama la proc associata all’oggetto, quella che nell’esempio scrive a video: Finalizing 20805910. L’id dell’oggetto è passato come parametro al blocco e noi lo mostriamo. Se andiamo a riesaminare il contenitore degli oggetti questa volta non conterrà nessuna istanza della classe E.

Nel prossimo articolo esaminerò l’utilizzo di metodi distruttori da associare all’evento di distruzione dell’oggetto. Tratterò anche la problematica legata alle variabili di istanza.

 
Comments Off

Posted in Ruby

 

Tags: , ,

Comments are closed.