I m trying to understand the Ruby Object Model. I understood that the instance methods are saved in the class rather than in the objects of the class because it removes redundan
Just to be super duper clear.
Here is a quick ruby script that explains the question:
#!/usr/bin/env ruby
puts ObjectSpace.count_objects[:T_CLASS] #>> 471
class X
def self.foo
end
def bar
end
end
puts ObjectSpace.count_objects[:T_CLASS] #>> 473
This is what the OP meant by "ObjectSpace.count_objects[:T_CLASS] increments the count by 2." Let's call the extra class the singleton class of X, because that appears to be what Ruby calls it internally.
irb> X
=> X
irb> X.singleton_class
=>
Notice that the #foo
method is an instance method of X.singleton_class
, not X
.
irb> X.instance_methods(false)
=> [:baz]
irb> X.singleton_class.instance_methods(false)
=> [:foo]
So why is :foo
stored in X.singleton_class
instead of X
? Isn't there only ever going to be one X
?
I believe the main reason is for consistency. Consider the following, simpler scenario concerning plain instance objects.
car = Car.new
def car.go_forth_and_conquer
end
As @mikej explained superbly, this new method is stored in car's singleton class.
irb> car.singleton_class.instance_methods(false)
=> [:go_forth_and_conquer]
Now, classes are objects too. Each class is an instance of Class
. Thus, when a class (say, X
) is defined, ruby is really creating an instance of Class
, and then adding methods to the instance (similar to what we did to car
above.) For example, here is an alternative way to create a new class
Car = Class.new do
def go_forth_and_conquer
puts "vroom"
end
end
Car.new.go_forth_and_conquer
Therefore, it is much easier to just reuse the code and do it the same way (i.e. keep foo
in X.singleton_class
.) This probably requires less effort and will lead to fewer surprises, so no one will ever need to write code to handle Class
instances differently from other instances.
You might be thinking that if Ruby did not have singleton classes for instances of Class
, there could be some memory savings. However, it sounds to me that where bar
is actually stored is an implementation detail that we probably shouldn't count on. Rubinius, MRI, and JRuby could all store methods and instances differently, as long as the behavior is consistent. For all we know, there could be a reasonable implementation of Ruby that doesn't eagerly create singleton classes for class objects, for the exact same reasons you outlined, as long as the overall behavior conforms to the ruby spec. (E.g. an actual singleton class doesn't exist until the #singleton_class
method is first invoked.)