我需要偶尔在缓存目录中使用rmagick创建图像。
然后快速摆脱它们而不丢失视图,我想删除图像文件,同时我的图像类的Ruby实例被破坏或进入垃圾收集。
我必须覆盖哪些ClassMethod来为析构函数提供代码?
答案 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对象,将它们退出,并在客户端使用它们时在其范围的末尾明确地销毁它们(从客户端的角度来看是隐含的)。