加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Ruby元类:为什么三个定义单例方法时?

发布时间:2020-12-17 03:37:48 所属栏目:百科 来源:网络整理
导读:让我们计算MRI范围内的类: def count_classes ObjectSpace.count_objects[:T_CLASS]endk = count_classes 使用类方法定义类: class A def self.foo nil endend 并运行: puts count_classes - k#= 3 请解释一下,为什么三个? 解决方法 看看MRI代码,每当你
让我们计算MRI范围内的类:

def count_classes
  ObjectSpace.count_objects[:T_CLASS]
end
k = count_classes

使用类方法定义类:

class A
  def self.foo
    nil
  end
end

并运行:

puts count_classes - k
#=> 3

请解释一下,为什么三个?

解决方法

看看MRI代码,每当你创建一个在Ruby中是Class类型对象的类时,ruby会自动为该新类创建“元类”类,这是另一个单例类型的Class对象.

C函数调用(class.c)是:

rb_define_class
  rb_define_class_id
    rb_class_new(super);
    rb_make_metaclass(klass,RBASIC(super)->klass);

因此,每次定义一个新类时,Ruby都会定义另一个带有元信息的类.

当你定义一个类方法时,我的意思是,def self.method,在内部,ruby调用rb_define_singleton_method.您可以检查它是否执行以下步骤:

创建一个ruby文件test.rb:

class A
  def self.foo
  end
end

并运行以下命令:

ruby --dump insns test.rb

您将获得以下输出:

== disasm: <RubyVM::InstructionSequence:<main>@kcount.rb>===============
0000 trace            1                                               (  70)
0002 putspecialobject 3
0004 putnil
0005 defineclass      :A,<class:A>,0
0009 leave
== disasm: <RubyVM::InstructionSequence:<class:A>@kcount.rb>============
0000 trace            2                                               (  70)
0002 trace            1                                               (  71)
0004 putspecialobject 1
0006 putself
0007 putobject        :foo
0009 putiseq          foo
0011 opt_send_simple  <callinfo!mid:core#define_singleton_method,argc:3,ARGS_SKIP>
0013 trace            4                                               (  73)
0015 leave                                                            (  71)
== disasm: <RubyVM::InstructionSequence:foo@kcount.rb>==================
0000 trace            8                                               (  71)
0002 putnil
0003 trace            16                                              (  72)
0005 leave

define_singleton_method映射到rb_obj_define_method C函数(object.c),它执行以下调用:

rb_obj_define_method
   rb_singleton_class(obj)
   rb_mod_define_method

函数rb_singleton_class公开了在定义类时创建的元类,但它也为此元类创建了一个新的元类.

根据这个函数的Ruby文档:“如果一个obj是一个类,返回的单例类也有自己的单例类,以保持元类的继承结构的一致性”.

这就是为什么在定义类方法时类的数量增加1的原因.

如果通过以下方式更改代码,则会发生相同的效果

class A
end
A.singleton_class

singleton_class映射到rb_obj_singleton_class C函数,该函数调用rb_singleton_class.

即使您创建了一个类方法并调用了singleton_class方法,创建的类的数量也不会改变,因为已经创建了处理元信息所需的所有类.例:

class A
  def self.foo
    nil
  end
end

A.singleton_class

上面的代码将继续返回3.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读