删除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 – 这意味着我不能在那里使用self
或self.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