【问题标题】:Deleting an object in Ruby在 Ruby 中删除对象
【发布时间】:2012-02-23 02:43:51
【问题描述】:

假设我有以下课程:

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) 会调用 someProc Vehicle 实例被销毁 - 这意味着我不能在那里使用selfself.class(因为没有类,因为有没有对象!)我可以让 proc 调用 Vehicle 类上的公共访问器方法,但这消除了类变量只能由类及其实例访问的目的 -> 本质上是将类变量转换为 gvar。

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

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

【问题讨论】:

标签: ruby garbage-collection finalizer


【解决方案1】:

您几乎肯定想要的是标准库中的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

【讨论】:

    【解决方案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>]
    

    如果您运行此代码,您将看到它“工作”,除了剩下的车辆没有被垃圾收集。我不确定这是为什么。

    您的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
    

    【讨论】:

    • 在阅读有关 Ruby 终结器的信息时,我发现这很好 article,它描述了您的代码的一个问题(这实际上可以解释剩余的神秘 1 个实例):您的 define_finalizer 方法的一个问题是终结器 proc 是一个闭包,其中包含对其 self 的引用,因此无法对图像对象进行垃圾收集。
    • @GregoryGoltsov:实际上,我对此很小心。终结器 proc 是 Vehicle.method(:finalize),它只包含对类 Vehicle 的引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 2011-03-08
    • 2011-03-26
    相关资源
    最近更新 更多