只是为了超级清楚。
这是一个解释问题的快速 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
(例如,在第一次调用该方法之前,实际的单例类不存在。)