只是为了超级清楚。
这是一个解释问题的快速 ruby 脚本:
#!/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
这就是“ObjectSpace.count_objects[:T_CLASS] 将计数增加 2”的 OP 含义。让我们将额外的类称为 X 的单例类,因为这似乎是 Ruby 内部对其的称呼。
irb> X
=> X
irb> X.singleton_class
=> <Class: X>
请注意,该#foo方法是 的实例方法X.singleton_class,而不是X。
irb> X.instance_methods(false)
=> [:baz]
irb> X.singleton_class.instance_methods(false)
=> [:foo]
那么为什么:foo存储在X.singleton_class而不是X?不是只有一个X吗?
我认为主要原因是一致性。考虑以下关于普通实例对象的更简单的场景。
car = Car.new
def car.go_forth_and_conquer
end
正如@mikej 出色地解释的那样,这个新方法存储在汽车的单例类中。
irb> car.singleton_class.instance_methods(false)
=> [:go_forth_and_conquer]
类是对象
现在,类也是对象。每个类都是一个实例Class。因此,当X定义了一个类(比如Class,car
Car = Class.new do
def go_forth_and_conquer
puts "vroom"
end
end
Car.new.go_forth_and_conquer
因此,仅重用代码并以相同的方式执行(即保留foo在X.singleton_class.Class实例。
可能无关紧要
您可能会想,如果 Ruby 没有用于 实例的单例类Class,那么可能会节省一些内存。但是,在我看来,bar实际存储的位置是我们可能不应该指望的实现细节。只要行为一致,Rubinius、MRI 和 JRuby 都可以不同地存储方法和实例。据我们所知,只要整体行为符合 ruby 规范,出于与您概述的完全相同的原因,可能有一个合理的 Ruby 实现不会急切地为类对象创建单例类。#singleton_class(例如,在第一次调用该方法之前,实际的单例类不存在。)