Viniendo de otros lenguajes de programación, cuando empecé con Ruby echaba de menos los métodos destructores de objetos. Igual que existe un método initialize, esperaba que hubiera un método finalize al que se invocase justo antes de que el garbage collector pasase a limpiar los objetos no usados.
Buscando un poco, di con la clase ObjectSpace, con la que se pueden hacer algunas cosas interesantes, como por ejemplo recorrer todos los objetos que existen en memoria en un momento determinado. Esta clase tiene un método, define_finalizer, que permite definir un hook que se llamará cuando el objeto se libere.
El problema es que, por como está implementado, cuando se invoca a este hook el objeto ya no puede existir en memoria, porque si existiese habría una referencia a ese objeto y entonces el garbage collector no lo limpiaría nunca. La verdad es que este comportamiento es bastante mejorable y, como bien dice aquí Charles Nutter, no es lo único mejorable de la clase ObjectSpace.
El caso es que ese comportamiento es el que hay, y con eso tenemos que jugar. Para el caso más sencillo, es tan simple como lo siguiente.
a='cadena de prueba' ObjectSpace.define_finalizer(a,proc{|id| puts "finalizando el objeto #{id}"}) a=nil GC.start
Si ejecutamos eso en la consola, podremos ver cómo, cuando pasamos el collector, se muestra la cadena indicando que se ha destruído.
Si lo que queremos es llamar a un método concreto, hay que tener en cuenta que el objeto ya se habrá destruído cuando se invoque su finalizador, así que podremos llamar solamente a métodos no asociados a la instancia. Lo normal en ese caso es llamar a métodos de la clase de este objeto. Por ejemplo
class TestClass def initialize ObjectSpace.define_finalizer(self,self.class.method(:terminate).to_proc) end def TestClass.terminate(object_id) puts "se ha destruÃ�­do el objeto {#object_id}" end end
En el método terminate podríamos hacer lo que necesitásemos, siempre teniendo en cuenta que el objeto ya no existe. Si se tratase de liberar recursos abiertos a la hora de inicializarlo, que es el caso más común, se podría usar una Hash definida como variable de clase y guardar la referencia a los recursos que se abren usando el object_id como clave. De este modo, al llamar al método terminate, podríamos acceder con el id a esa Hash para poder liberar el recurso que nos interese.
En cualquier caso, hay que decir en defensa de Ruby que para muchos de los casos donde en otros lenguajes de programación usaríamos destructores de objetos, en Ruby usaremos bloques, quedando un código mucho más legible.
Por ejemplo
FantasticConnection.open(end_point) do |conn| conn.send(my_data) end
searchwords: finalizer, finalize, destructor, constructor, garbage collector, colector de basura, ObjectSpace