Deleting an object in Ruby

久未见 提交于 2019-12-01 02:13:52

Right now, they will never be garbage collected, as you are holding a reference in @@all_instances. You could use a finalizer to get the result you want:

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>]

If you run this code, you will see that it "works", except that there remains one Vehicle that is not garbage collected. I'm not sure why that is.

Your count method could be also defined more simply by returning ObjectSpace.each_object(Vehicle).count

Finally, if you really want to maintain a list of existing Vehicles, you need to store their ID and use 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

What you almost certainly want here is the WeakRef class from the standard library. That handles all the details of object tracking and management without blocking reference counting.

Using a WeakRef that points to the object in your tracking you can delegate the whole finalization work to the library, and simplify your own life. (You may need to flush dead items from the arrays, but that is easily enough wrapped in your parent class.)

eg:

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!