Ruby:析构函数?

时间:2011-05-10 20:29:38

标签: ruby destructor

我需要偶尔在缓存目录中使用rmagick创建图像。

然后快速摆脱它们而不丢失视图,我想删除图像文件,同时我的图像类的Ruby实例被破坏或进入垃圾收集。

我必须覆盖哪些ClassMethod来为析构函数提供代码?

7 个答案:

答案 0 :(得分:25)

@ 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

答案 1 :(得分:20)

您可以在创建图像文件时使用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 :(得分:10)

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

块完成执行后,图像被销毁。只需启动一个块,在里面进行所有图像处理,然后让图像自行破坏。这类似于以下C ++示例:

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

答案 3 :(得分:3)

Ruby有ObjectSpace.define_finalizer来设置对象的终结器,但它的使用并不完全受到鼓励,而且相当有限(例如,终结器不能引用它所设置的对象,否则终结器将呈现对象不符合垃圾回收的条件)。

答案 4 :(得分:2)

Ruby中没有析构函数。

您可以做的只是清除任何不再打开的文件,或者使用为您执行此操作的TempFile类。

<强>更新

我之前声称PHP,Perl和Python没有析构函数,但是这似乎是错误的,因为igorw指出。不过,我还没有经常使用它们。正确构造的析构函数在任何基于分配的语言中都是必不可少的,但在收集垃圾的语言中,它最终是可选的。

答案 5 :(得分:0)

您的问题有一个非常简单的解决方案。 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

答案 6 :(得分:-1)

在Ruby中实现与Python的上下文管理器类似的东西:

#!/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。 Manager是一个高级类,它从客户端接收Customer对象,将它们退出,并在客户端使用它们时在其范围的末尾明确地销毁它们(从客户端的角度来看是隐含的)。