假设我有以下课程:
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,当调用该对象的垃圾收集时,它将被调用。但是根据文档,{/ 1}}会在之后调用someProc 来实现Vehicle实例的销毁 - 这意味着我不能在那里使用ObjectSpace.define_finalizer(v,someProc)
或self
(因为会有没有类,因为没有对象!)我可以让proc在Vehicle类上调用一个公共访问器方法,但这样就消除了类变量只能访问类及其实例的目的 - &gt;基本上将类变量转换为gvars。
如何在进行垃圾回收之前获得相似的析构函数方法(来自C ++),以便按顺序获取Vehicle实例的事务?
P.S。
self.class
不是一个可行的选择,因为即使Ruby文档也是关于它的。
答案 0 :(得分:3)
您几乎肯定想要的是标准库中的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
答案 1 :(得分:2)
现在,它们永远不会被垃圾收集,因为你在@@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:0x0000010208e730>]
如果您运行此代码,您将看到它“正常工作”,除了仍有一个非垃圾收集的Vehicle。我不确定为什么会这样。
还可以通过返回count
ObjectSpace.each_object(Vehicle).count
方法
最后,如果您真的想维护现有的车辆列表,则需要存储其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