Может ли один объект Ruby уничтожить другой? - PullRequest
6 голосов
/ 24 декабря 2009

В Ruby один объект может уничтожить другой?

Например:

class Creature
  def initialize
    @energy = 1
  end
  attr_accessor :energy
 end

class Crocodile < Creature
  def eat(creature)
    @energy += creature.energy
    creature = nil #this does not work
  end
end

fish = Creature.new
croc = Crocodile.new
croc.eat(fish)

После того, как крокодил съел существо и поглотило его энергию, существо должно перестать существовать. Но приведенный выше код не уничтожает существо.

Я знаю, что если я скажу fish = nil, объект, на который ссылается переменная fish, будет собран мусором. Но сказать, что creature = nil внутри метода Крокодила eat, этого не добиться.

Еще один способ выразить это

Изнутри croc.eat, могу ли я сказать "поскольку переменная 'fish' была передана мне, когда я закончу, я собираюсь установить 'fish' в ноль?"

Обновление: проблема решена

Я, по сути, принял подход, предложенный Чаком, с некоторыми изменениями. Вот мои рассуждения:

  1. Если переменная, указывающая на объект, больше не будет, это будет сборщик мусора
  2. Если при создании объекта я добавляю его в хеш (например, «x» => объект) и не создаю для него никакой другой переменной, то удаление этого элемента из хеша приводит к сбору мусора объект
  3. Кажется логичным, что список всех существ должен храниться в классе Существа

Поэтому я сделал это:

  1. В объекте класса Creature я создал хеш и присвоил его переменной экземпляра. Мы назовем это @creaturelist. (Причина, по которой я использовал переменную экземпляра, а не переменную класса, заключается в том, что любой подкласс Creature также может иметь свой собственный список.)
  2. В методе Initialize новое существо передает себя в класс Существа
  3. Класс Существо добавляет ссылку на это существо к @creaturelist и возвращает идентификатор существу.
  4. Существо запоминает этот идентификатор в своей собственной переменной @id.
  5. Если существо умирает, оно вызывает родительский класс с Creature.remove(@id), и единственная ссылка на него удаляется.

Теперь я могу сделать это:

class Predator < Creature
  def eat(creature)
    @energy += creature.energy
    creature.die
  end
end

fish = Creature.new
Creature.list #shows the fish
croc = Predator.new
croc.eat(fish)
Creature.list #no more fish

Конечно, в этом примере fish по-прежнему указывает на этот объект существа, поэтому он не является сборщиком мусора. Но в конце концов, существа будут создаваться и есть друг друга по правилам, поэтому я не буду называть их по отдельности.

Ответы [ 5 ]

7 голосов
/ 24 декабря 2009

Мне кажется, проблема в том, что вы думаете о самой программе как о мире, в котором эти симулированные вещи живут, а не симулируют.

fish = Creature.new
croc = Crocodile.new
$world = [fish, croc]
class Crocodile
  def eat(creature)
    @energy += creature.energy
    $world.delete creature
  end
end
croc.eat fish
world # [croc], now all by his lonesome, the last creature in the world :(

И если предположить, что переменная fish вышла из области видимости, как это было бы в правильно структурированной программе, этот объект, скорее всего, теперь будет мусором.

2 голосов
/ 24 декабря 2009

Ничто не может быть безопасно собрано до тех пор, пока в какой-либо активной области нет ссылок на него.

1 голос
/ 24 декабря 2009

croc.eat (fish) обнулит ссылку croc на Существо, на которое ссылается fish, но учтите, что сама переменная "fish" по-прежнему содержит ссылку на это Существо, поэтому экземпляр не является мусором.

edit: Думайте об этом так: внутри крокодила вы не получаете рыбу, вы получаете копию того, что находится внутри рыбы. Значение fish - это ссылка на объект, который вы создали с помощью Creature.new. Копия этой ссылки копируется в переменную существо, когда вы запускаете croc.eat (fish). Так что теперь и у рыбы, и у существа есть ссылки на один и тот же объект.

Это похоже на воздушный шар, плавающий в воздухе, с двумя привязанными к нему струнами. рыба держит одну строку, а существо держит другую строку. когда вы устанавливаете существо на ноль, оно отпускает удержание на воздушном шаре, но рыба все еще держится за воздушный шар своей собственной цепочкой, поэтому воздушный шар не уплывает к большому сборщику мусора в небе.

edit 2: Нет, вы не можете (без некоторой глубокой магии, которую было бы очень плохой идеей выполнить) протянуть руку, чтобы ловить рыбу и уничтожить ее ссылку на рассматриваемый объект.

0 голосов
/ 24 декабря 2009

Один объект Ruby (обычно) не должен уничтожать другой. Ваша задача не беспокоиться о том, существуют ли объекты до сих пор или нет. Единственное исключение будет, если вы используете хранилище базы данных - там вы можете позаботиться о том, удаляются объекты или нет.

Но, в общем, мой ответ таков: вас это не должно волновать. (Другие ответы, тем не менее, имеют смысл.)

0 голосов
/ 24 декабря 2009

Вы не «уничтожаете объект» - GC делает это за вас. Вы говорите о том, чтобы быть в методе, и о том, как выйти в область действия вызывающего и изменить там привязку.

Если привязка была частью объекта, и вы передали объект внутрь, вы можете переназначить его (на ноль) оттуда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...