为什么 ruby 在创建 class 之后创建 3 个对象?

Why does ruby create 3 objects after a class is created?

我正在研究 Ruby 的 metaclass。我 read this answer 很好地描述了 metaclass 是什么。它在创建 class 时显示在那里,它将创建两个对象。这是可以理解的。一种用于 class 本身,另一种用于元 class。但是当我自己尝试时,我发现它正在创建三个对象。

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

我正在使用 Ruby - 2.5.1

谁能帮我理解这个?

更新:

我添加的 reference SO post 使用 ruby-1.9.1 或更高版本,因为 ObjectSpace 的方法 count_objects 已在中引入1.9.1.似乎 T_CLASS 计数一直都是 3(用 ruby-1.9.3-p551 试过)。

所以,到现在为止,为什么 this answer. Ruby under a microscope 也说计数是 2 仍然是个谜。

来自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.

稍微修改一下问题代码:

$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

我得到以下结果:

Counts: 690
Test class created
New classes: [Test]
Counts: 692
Test singleton class referenced
New classes: [#<Class:Test>]
Counts: 693

我在控制台内部和外部使用 Ruby 2.6 和 2.0 进行了尝试(数字不同但差异相同),@SajibHassan 使用 1.9.3(方法 [=12 的版本) =] 被引入)。 这意味着差异始终为 3,并且用户无法访问创建的第一个单例 class。

Ruby Under a Microscope(写于2012年Ruby 2.1发布后)也描述了只有两个metaclasses的创建,这与我们的结果不符得到。

请注意,像 Module#prepend(在 Ruby 2.0 中引入)这样的方法,@JörgWMittag 在评论中将其作为此额外 class 的可能原因提到,请使用 T_ICLASS。查看 the commit in which the method was introduced 了解详情。我猜 T_ICLASS 代表内部 class,因此内部 classes 不应该对用户可见(这是有道理的)。我不确定为什么有些 T_CLASS 可供用户访问而有些则不能。