在Ruby中删除一个对象 [英] Deleting an object in Ruby

查看:125
本文介绍了在Ruby中删除一个对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下班级:

  class Vehicle 
@@ total_vehicles = 0
@@ all_instances = Array.new

def初始化
@@ total_vehicles + = 1
@@ all_instances<< self
end

def total_vehicles #returns'alive'总数'
return @@ total_vehicles
end

def all_vehicles #returns所有Vehicle对象的数组
return @@ all_instances
end

end

现在保持 @@ total_vehicles @@ all_instances 最新且正确,我想确保它们分别正确递减和更新,当其中一个对象被垃圾收集时。但是这里发生了什么:

  v = Vehicle.new 
Vehicle.total_vehicles#=> 1
v = nil#现在没有引用Vehicle实例
ObjectSpace.garbage_collect#实例垃圾收集
Vehicle.total_vehicles#=> 1没有!

那么我可以为每个Vehicle类实例添加一个终结器Proc,当调用对象的垃圾收集,将被调用。但根据文档,在Vehicle实例被销毁之后, ObjectSpace.define_finalizer(v,someProc)会调用someProc - 这意味着我不能使用 self self.class 在那里(因为没有类,因为没有对象!)我可以有proc在Vehicle类上调用一个公共访问器方法,但是这会带走只能通过类及其实例访问的类变量的目的 - >本质上将类变量转换为gvars。

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



PS
ObjectSpace#count_objects 并不是一个可行的选择,因为即使是Ruby文档也在前面。

解决方案

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

  class Vehicle 
class<< self
attr_accessor:count
def finalize(id)
@count - = 1
end

def all#返回所有Vehicle对象的数组
ObjectSpace.each_object(Vehicle).to_a
end
end
Vehicle.count || = 0

def初始化
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,不知道为什么
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。新的
@ids<< obj.object_id
ObjectSpace.define_finalizer(obj,method(:finalize))
end

def all#返回一个包含所有Vehicle对象的数组
@ ids.map {| ID | ObjectSpace._id2ref(id)}
end
$ b $ def def count
@ ids.size
end
end

def initialize
Vehicle.register(self)
end
end


Let's say I have the following class:

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

Now to keep @@total_vehicles and @@all_instances up-to-date and correct, I want to make sure that they are correctly decremented and updated, respectively, when one of those objects is garbage collected. But here is what happens:

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!

Well I could add a finalizer Proc to each instance of the Vehicle class that, when called upon the object's garbage collection, would be called. But according to the documentation, ObjectSpace.define_finalizer(v,someProc) would call someProc after the Vehicle instance is destroyed - meaning I cannot use self or self.class in there (since there would be no class, as there is no object!) I could have the proc call a public accessor method on the Vehicle class, but that takes away the purpose of class variables being accessible only to the class and its instances -> essentially turning the class variables into gvars.

How can I have the equivalent of a destructor method (from C++) that will get a Vehicle instance's affairs in order, as it were, before getting garbage-collected?

P.S. ObjectSpace#count_objects is no a viable option, as even the Ruby docs are up front about.

解决方案

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

这篇关于在Ruby中删除一个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆