Подтвердить что ты не робот

Рубин: Деструкторы?

Мне нужно иногда создавать изображения с помощью rmagick в директории cache.

Чтобы быстро избавиться от них, не теряя их для представления, я хочу удалить файлы изображений, в то время как мой экземпляр Ruby класса Image разрушается или входит в коллекцию Garbage.

Какой ClassMethod я должен переписать для подачи деструктора с кодом?

4b9b3361

Ответ 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 с ключевым словом. Менеджер - это класс высокого уровня, который получает объекты Клиента от клиента, возвращает их обратно и явно уничтожает их в конце своей области, когда клиент выполняется с их использованием (что подразумевается с точки зрения клиента).