Can one Ruby object destroy another?
In Ruby, can one object destroy another?
For example:
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)
After the crocodile has eaten a creature and absorbed its energy, the creature should cease to exist. But the code above doesn't destroy the creature.
I know that if I say fish = nil
, the object that the varible fish
refers to will be garbage collected. But saying creature = nil
inside the Crocodile's eat
method doesn't accomplish that.
Another way of putting it
From inside croc.eat, can I say "since the variable 'fish' was passed to me, when I'm done, I'm going to set 'fish' to nil?"
Update: problem solved
I've essentially taken the approach that Chuck suggested, with some modifications. Here was my reasoning:
- If there is no longer any variable pointing to an object, it will be garbage collected
- If, when an object is created, I add it to a hash (like 'x' => object), and don't create any other variable for it, then deleting that item from the hash results in garbage collecting the object
- It seems logical that a list of all creatures should be stored in the Creature class
Therefore, I did this:
- On the Creature class object, I created a hash and assigned it to an instance variable. We'll call it
@creaturelist
. (The reason I used an instance variable and not a class variable is so that any subclass ofCreature
can have its own list, too.) - In the Initialize method, a new creature hands itself to the Creature class
- The Creature class adds a reference to that creature to
@creaturelist
and returns an ID to the creature. - The creature remembers that ID in its own
@id
variable. - If the creature dies, it calls the parent class with
Creature.remove(@id)
, and the only reference to itself gets deleted.
Now I can do this:
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
Of course, in this example, fish
still points to that creature object, so it's not garbage collect开发者_Python百科ed. But eventually, creatures will be created and eat each other based on rules, so I won't be individually naming them.
I think the problem is that you're thinking of the program itself as the world in which these simulated things live rather than simulating one.
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 :(
And assuming the fish
variable had gone out of scope like it would in a properly structured program, that object would most likely now be garbage.
Nothing can be safely garbage collected until there are no references to it in any active scope.
croc.eat(fish) will nil out croc's reference to the Creature referenced by fish, but note the variable "fish" itself still holds a reference to that Creature, so the instance is not garbage.
edit: Think about it this way: Inside of croc, you're not getting fish, you're getting a copy of what's inside of fish. fish's value is a reference to the object you created with Creature.new. A copy of that reference is copied into the variable creature when you execute croc.eat(fish). So now both fish and creature have references to the same object.
It's like there's a balloon floating in the air, with two strings tied to it. fish is holding one string, and creature is holding another string. when you set creature to nil, it releases its hold on the balloon, but fish is still holding onto the balloon by its own string, so the balloon doesn't float away to the big garbage collector in the sky.
edit 2: No, you can't (without some deep magic that it would be a very bad idea to perform), reach out to fish and wipe out its reference to the object in question.
You aren't "destroying an object" -- the GC does that for you. You're talking about being in a method, and reaching out into the caller's scope and changing a binding there.
If the binding was part of an object, and you passed the object in, you could reassign it (to nil) from there.
One Ruby object (usually) shouldn't destroy another. It's not your job to worry about whether objects still exist or not. The one exception would be if you are using a database store - there you might care about whether the objects are deleted or not.
But in general, my answer is: you shouldn't care about this. (The other answers do make sense, though.)
精彩评论