删除Ruby中的对象

假设我有以下课程:

class Vehicle @@total_vehicles = 0 @@all_instances = Array.new def initialize @@total_vehicles += 1 @@all_instances << self end def total_vehicles #returns total number of Vehicles 'alive' return @@total_vehicles end def all_vehicles #returns an array of all Vehicle objects return @@all_instances end end 

现在让@@total_vehicles@@all_instances保持最新和正确,我想确保在其中一个对象被垃圾收集时分别正确地减少和更新它们。 但这是发生的事情:

 v = Vehicle.new Vehicle.total_vehicles # => 1 v = nil #no references to Vehicle instance now ObjectSpace.garbage_collect #instance garbage collected Vehicle.total_vehicles # => 1 Nope! 

好吧,我可以为Vehicle类的每个实例添加一个终结器Proc,当调用该对象的垃圾收集时,它将被调用。 但根据文档, ObjectSpace.define_finalizer(v,someProc)会在Vehicle实例被销毁调用someProc – 这意味着我不能在那里使用selfself.class (因为没有类,因为没有对象!)我可以让proc在Vehicle类上调用一个公共访问器方法,但这样就消除了类变量只能访问类及其实例的目的 – >基本上将类变量转换为gvar。

我怎样才能得到一个析构函数方法(来自C ++),它会在获取垃圾收集之前按顺序获取Vehicle实例的事务?

PS ObjectSpace#count_objects不是一个可行的选择,因为即使是Ruby文档也是如此。

现在,它们永远不会被垃圾收集,因为你在@@all_instances中持有一个引用。 您可以使用终结器来获得所需的结果:

 class Vehicle class << self attr_accessor :count def finalize(id) @count -= 1 end def all #returns an array of all Vehicle objects ObjectSpace.each_object(Vehicle).to_a end end Vehicle.count ||= 0 def initialize Vehicle.count += 1 ObjectSpace.define_finalizer(self, Vehicle.method(:finalize)) end end 100.times{Vehicle.new} p Vehicle.count # => 100 ObjectSpace.garbage_collect p Vehicle.count # => 1, not sure why p Vehicle.all # => [#] 

如果您运行此代码,您将看到它“正常工作”,除了仍有一个非垃圾收集的Vehicle。 我不确定为什么会这样。

您的count方法也可以通过返回ObjectSpace.each_object(Vehicle).count来更简单地定义

最后,如果您真的想要维护现有Vehicle的列表,则需要存储其ID并使用ObjectSpace._id2ref

 require 'set' class Vehicle class << self def finalize(id) @ids.delete(id) end def register(obj) @ids ||= Set.new @ids << obj.object_id ObjectSpace.define_finalizer(obj, method(:finalize)) end def all #returns an array of all Vehicle objects @ids.map{|id| ObjectSpace._id2ref(id)} end def count @ids.size end end def initialize Vehicle.register(self) end end 

你几乎肯定想要的是标准库中的WeakRef类。 它处理对象跟踪和管理的所有细节,而不会阻止引用计数。

使用指向跟踪中对象的WeakRef,您可以将整个完成工作委派给库,并简化您自己的生活。 (您可能需要从数组中刷新死项,但这很容易包装在父类中。)

例如:

 def all_instances # this will vacuum out the dead references and return the remainder. @@weakrefs_to_vehicles = @@weakrefs_to_vehicles.select(&:weakref_alive?) end def total_vehicles all_instances.count end