I was studying about Ruby's metaclass. I read this answer where it is nicely described what metaclass is. It's showed there when a class is created it will create two objects. Which is understandable. One for the class itself and one for it's metaclass. But when I am trying it myself I see that it is creating three objects.
puts "Before Class Creation object count - #{ObjectSpace.count_objects[:T_CLASS]}"
class Test
def self.foo # test_singleton
p 'Printed from method #foo'
end
def bar # test
p 'Printed from method #bar'
end
end
puts "After Class Creation object count - #{ObjectSpace.count_objects[:T_CLASS]}"
###############
Before Class Creation object count - 949
After Class Creation object count - 952
I am using Ruby - 2.5.1
.
Can anyone help me understand this one?
Update:
The reference SO post that I added is using ruby-1.9.1 or greater, as the method count_objects
for ObjectSpace
was introduced in 1.9.1. It seems that the T_CLASS
count has always always been 3 (tried with ruby-1.9.3-p551
).
So, till now it's still a mystery why this answer. Ruby under a microscope also says the count is 2.
An object is a unit of data. A class is what kind of data it is.
Everything in Ruby is an object. All objects have an identity; they can also hold state and manifest behaviour by responding to messages. These messages are normally dispatched through method calls. A string is an example of a Ruby object.
It is called whenever a new object is created. Whenever new class method called it always call initialize instance method. initialize method is like a constructor, whenever new objects are created initialize method called.
From https://bugs.ruby-lang.org/issues/16788:
Creating a class automatically creates a singleton class (which is not accessible to the user). Referencing the singleton class of a class automatically creates a singleton class of that singleton class. This is to keep consistency of the inheritance structure of metaclasses. Otherwise, class methods wouldn't inherit from the superclass's metaclass, which is necessary as the superclass's class methods should be available as the subclass's class methods.
Modifying the question code a bit:
$old_classes = []
def print_objects
new_classes = []
ObjectSpace.each_object(Class){|x| new_classes << x}
puts "New classes: #{new_classes - $old_classes}" unless $old_classes.empty?
puts "Counts: #{ ObjectSpace.count_objects[:T_CLASS] }"
$old_classes = new_classes
end
print_objects
class Test
end
puts 'Test class created'
print_objects
class Test
def self.foo
end
end
puts 'Test singleton class referenced'
print_objects
I get the following results:
Counts: 690
Test class created
New classes: [Test]
Counts: 692
Test singleton class referenced
New classes: [#<Class:Test>]
Counts: 693
I tried it with Ruby 2.6 and 2.0 both inside and outside a console (the numbers differ but the difference is the same) and @SajibHassan with 1.9.3 (version in which the method count_objects
was introduced).
This means that the difference has always been 3 and that the first singleton class created is not accessible for the user.
The book Ruby Under a Microscope (written in 2012 after the release of Ruby 2.1) also describes the creation of only two metaclasses, which doesn't match the result we get.
Note that methods like Module#prepend
(introduced in Ruby 2.0), which was mentioned by @JörgWMittag in the comments as the possible reason for this extra class, use T_ICLASS
. Check the commit in which the method was introduced for details. I guess that T_ICLASS
stands for internal class and consequently internal classes shouldn't be visible to the user (which makes sense). I am not sure though why some T_CLASS
are accessible to the user and some others are not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With