Destruktory w Rubim, czyli ObjectSpace w akcji
Posted by Jacek Galanciak on Aug 13 2007
Ruby (podobnie jak Java) nie posiada destruktorów. Może to się wydawać dziwne, ale okazuje się, że garbage collector załatwia za nas większość przykrych czynności, a pozostałe można zrealizować poprzez odpowiednią budowę aplikacji.
A co, jeśli się uprzemy na destruktor lub po prostu metodę, która zostanie wykonana podczas niszczenia obiektu?
“Destruktory” dla egzemplarza
one = "jeden"
two = "dwa"
three = "trzy"
ObjectSpace.define_finalizer(one, lambda {|id| puts "\"one\" (#{id}) umarł w męczarniach" })
ObjectSpace.define_finalizer(two, lambda do |id|
puts "\"two\" (#{id}) umarł w męczarniach"
end)
"two" (22660860) umarł w męczarniach
"one" (22660870) umarł w męczarniach
Trzeci też umarł, ale bardziej po cichu.
“Destruktory” dla klasy
Należy tutaj pamiętać, że definiujemy metodę klasy, a nie instancji.
class TestClass
def initialize
ObjectSpace.define_finalizer(self, self.class.method(:finish_him!).to_proc)
end
def TestClass.finish_him!(id)
puts "Flawless victory (#{id})"
end
end
test = TestClass.new
Wymuszenie garbage collectora
Moduł ObjectSpace
to nic innego jak interakcja z garbage collectorem, który działa w pełni poprawnie, ale automatycznie - nigdy nie wiemy, kiedy zostanie wywołany. Możemy jednak wywołać metodę garbage_collect
, by bardziej kontrolować pracę kolektora. Niestety, niszczymy wówczas wszystkie nieużywane obiekty, ale to chyba nie jest duży (o ile w ogóle) mankament.
class TestClass
def initialize
ObjectSpace.define_finalizer(self, self.class.method(:finish_him!).to_proc)
end
def TestClass.finish_him!(id)
puts "Flawless victory (#{id})"
end
end
def create_garbage
TestClass.new
end
puts "Tworzę śmieci"
create_garbage
puts "Usuwam śmieci."
ObjectSpace.garbage_collect
puts "Gotowe."
Tworzę śmieci
Usuwam śmieci.
Flawless victory (22660260)
Gotowe.
Warto pamiętać, że sprzątaniu ulegną śmieci powstałe w innych blokach niż ten, w którym poprzez wymuszenie wywoływany jest kolektor.
Pozostaje tylko pytanie: po co wymuszać? Jest to uzasadnione w naprawdę niewielu przypadkach. Innymi słowy: łapy precz. To tylko ciekawostka ;).
Ups…
ObjectSpace
posiada metodę _id2ref
, która zwraca referencję obiektu o podanym id.
ObjectSpace._id2ref("test".object_id) # => "test"
Kiedy wywoływany jest finalizer, przekazywany jest mu id obiektu, który właśnie został zniszczony. Szukanie referencji po takim identyfikatorze nie zadziała, bo obiektu już prawdopodobnie nie ma. Warto o tym pamiętać, by uniknąć frustrujących błędów na przyszłość.