Рубин: Деструкторы?
Мне нужно иногда создавать изображения с помощью rmagick в директории cache.
Чтобы быстро избавиться от них, не теряя их для представления, я хочу удалить файлы изображений, в то время как мой экземпляр Ruby класса Image разрушается или входит в коллекцию Garbage.
Какой ClassMethod я должен переписать для подачи деструктора с кодом?
Ответы
Ответ 1
Вы можете использовать ObjectSpace.define_finalizer
при создании файла изображения, и он будет вызван, когда мусорщик соберется. Просто будьте осторожны, чтобы не ссылаться на сам объект в вашем proc, иначе он не будет собран человеком мусора. (Не возьмешь что-то живое и ногами)
class MyObject
def generate_image
image = ImageMagick.do_some_magick
ObjectSpace.define_finalizer(self, proc { image.self_destruct! })
end
end
Ответ 2
Решение @edgerunner почти сработало. В принципе, вы не можете создать замыкание вместо вызова define_finalizer
, поскольку он фиксирует привязку текущего self
. В Ruby 1.8 кажется, что вы не можете использовать объект proc
, преобразованный (используя to_proc
) из метода, привязанного к self
. Чтобы он работал, вам нужен объект proc
, который не отображает объект, для которого вы определяете финализатор.
class A
FINALIZER = lambda { |object_id| p "finalizing %d" % object_id }
def initialize
ObjectSpace.define_finalizer(self, self.class.method(:finalize)) # Works in both 1.9.3 and 1.8
#ObjectSpace.define_finalizer(self, FINALIZER) # Works in both
#ObjectSpace.define_finalizer(self, method(:finalize)) # Works in 1.9.3
end
def self.finalize(object_id)
p "finalizing %d" % object_id
end
def finalize(object_id)
p "finalizing %d" % object_id
end
end
a = A.new
a = nil
GC.start
Ответ 3
Прикомы GC приятно читать, но почему не следует правильно освобождать ресурсы в соответствии с уже существующим синтаксисом языка?
Позвольте мне пояснить это.
class ImageDoer
def do_thing(&block)
image= ImageMagick.open_the_image # creates resource
begin
yield image # yield execution to block
rescue
# handle exception
ensure
image.destruct_sequence # definitely deallocates resource
end
end
end
doer= ImageDoer.new
doer.do_thing do |image|
do_stuff_with_image # destruct sequence called if this throws
end # destruct_sequence called if execution reaches this point
Изображение уничтожается после завершения выполнения блока. Просто запустите блок, сделайте всю обработку изображения внутри, а затем пусть изображение уничтожит себя. Это аналогично следующему примеру С++:
struct Image
{
Image(){ /* open the image */ }
void do_thing(){ /* do stuff with image */ }
~Image(){ /* destruct sequence */ }
};
int main()
{
Image img;
img.do_thing(); // if do_thing throws, img goes out of scope and ~Image() is called
} // special function ~Image() called automatically here
Ответ 4
Ruby имеет ObjectSpace.define_finalizer
для установки финализаторов на объекты, но его использование не рекомендуется и довольно ограничено (например, финализатор не может ссылаться на объект, для которого он установлен, или же финализатор сделает объект неприемлемым для сбора мусора).
Ответ 5
В Ruby действительно нет такой вещи, как деструктор.
Что вы можете сделать, это просто очистить любые файлы, которые больше не открыты, или использовать класс TempFile, который делает это для вас.
Обновление
Я ранее утверждал, что PHP, Perl и Python не имеют деструкторов, но это кажется ложным, как указывает igorw. Однако я не видел их очень часто. Правильно построенный деструктор необходим для любого языка, основанного на распределении, но в собранном мусоре он становится необязательным.
Ответ 6
Существует очень простое решение проблемы. Дизайн Ruby побуждает вас делать все действия определенным и понятным образом. Нет необходимости в магических действиях в конструкторе/деструкторе. Да, конструкторы необходимы как удобный способ назначения начального состояния объекта, но не для "волшебных" действий. Позвольте мне проиллюстрировать этот подход на возможном решении.
Цель, чтобы сохранить объекты изображения, но очистить файлы кеша изображений.
# you are welcome to keep an in memory copy of the image
# GC will take care of it.
class MyImage
RawPNG data
end
# this is a worker that does operations on the file in cache directory.
# It knows presizely when the file can be removed (generate_image_final)
# no need to wait for destructor ;)
class MyImageGenerator
MyImage @img
def generate_image_step1
@image_file = ImageLib.create_file
end
def generate_image_step2
ImageLib.draw @image_file
end
def generate_image_final
@img=ImageLib.load_image @image_file
delete_that_file @image_file
end
def getImage
# optional check image was generated
return @img
end
end
Ответ 7
Чтобы реализовать нечто подобное менеджеру контекста Python в Ruby:
#!/usr/bin/env ruby
class Customer
@@number_of_customers = 0
def initialize(id, name)
@_id = id
@_name = name
@@number_of_customers += 1
end
def self.get_number_of_customers()
return @@number_of_customers
end
def get_id()
return @_id
end
def get_name()
return @_name
end
def finalize()
@@number_of_customers -= 1
end
end
class Manager
def self.manage_customer(*custs, &block)
yield custs
custs.each do |c|
c.finalize()
end
end
end
Manager.manage_customer(Customer.new(0, 'foo'), Customer.new(1, 'bar')) do |custs|
puts("id is #{custs[0].get_id()}")
puts("id is #{custs[1].get_id()}")
puts("name is #{custs[0].get_name()}")
puts("name is #{custs[1].get_name()}")
puts("number of customers is #{Customer.get_number_of_customers()}")
end
puts("number of customers is #{Customer.get_number_of_customers()}")
Вкратце, здесь происходит то, что менеджер похож на использование Python с ключевым словом. Менеджер - это класс высокого уровня, который получает объекты Клиента от клиента, возвращает их обратно и явно уничтожает их в конце своей области, когда клиент выполняется с их использованием (что подразумевается с точки зрения клиента).