Ruby:析构函数?

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

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

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

您可以在创建图像文件时使用ObjectSpace.define_finalizer ,并在垃圾人收集时调用它。 注意不要在你的proc中引用对象本身,否则垃圾人不会收集它。 (不会拿起那些活着和踢的东西)

 class MyObject def generate_image image = ImageMagick.do_some_magick ObjectSpace.define_finalizer(self, proc { image.self_destruct! }) end end 

@ edgerunner的解决方案几乎奏效了。 基本上,你不能创建一个闭包代替define_finalizer调用,因为它捕获了当前self的绑定。 在Ruby 1.8中,似乎你不能使用从绑定到self的方法转换(使用to_proc )的任何proc对象。 要使其工作,您需要一个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 

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 

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

在Ruby中真的没有析构函数这样的东西。

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

更新

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

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

在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对象,将它们退出,并在客户端使用它们时在其作用域的末尾显式销毁它们(从客户端的角度来看是隐含的)。