Удаление объекта в Ruby - PullRequest
       13

Удаление объекта в Ruby

1 голос
/ 23 февраля 2012

Допустим, у меня есть следующий класс:

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!

Ну, я мог бы добавить Proc-финализатор для каждого экземпляра класса Vehicle, который при вызове объекта сборки мусора будет вызываться.Но согласно документации, ObjectSpace.define_finalizer(v,someProc) вызовет someProc после Экземпляр Vehicle будет уничтожен - это означает, что я не могу использовать self или self.class там (так как не будет никакого класса, так как нетобъект!) Я мог бы заставить proc вызывать публичный метод доступа к классу Vehicle, но это устраняет цель доступности переменных класса только для класса и его экземпляров -> по сути, превращая переменные класса в gvars.

Как я могу получить эквивалент метода-деструктора (из C ++), который будет приводить дела экземпляра Vehicle в порядок как бы до сбора мусора?

PS ObjectSpace#count_objects не является жизнеспособнымвариант, так как даже документы по Ruby заранее.

Ответы [ 2 ]

3 голосов
/ 23 февраля 2012

Здесь вам почти наверняка понадобится класс 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 голосов
/ 23 февраля 2012

Прямо сейчас, они никогда не будут собирать мусор, поскольку вы держите ссылку в @@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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...